highly experimental runtime model decimation + file cache adjustment to clean up processed file copies
This commit is contained in:
@@ -481,53 +481,13 @@ public sealed class CacheMonitor : DisposableMediatorSubscriberBase
|
||||
}
|
||||
}
|
||||
|
||||
long totalSizeExtras = 0;
|
||||
FileCacheSize = totalSize;
|
||||
|
||||
if (Directory.Exists(_configService.Current.CacheFolder + "/downscaled"))
|
||||
{
|
||||
var filesDownscaled = Directory.EnumerateFiles(_configService.Current.CacheFolder + "/downscaled").Select(f => new FileInfo(f)).OrderBy(f => f.LastAccessTime).ToList();
|
||||
totalSizeExtras += GetFolderSize(Path.Combine(_configService.Current.CacheFolder, "downscaled"), token, isWine);
|
||||
totalSizeExtras += GetFolderSize(Path.Combine(_configService.Current.CacheFolder, "decimated"), token, isWine);
|
||||
|
||||
long totalSizeDownscaled = 0;
|
||||
|
||||
foreach (var f in filesDownscaled)
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
long size = 0;
|
||||
|
||||
if (!isWine)
|
||||
{
|
||||
try
|
||||
{
|
||||
size = _fileCompactor.GetFileSizeOnDisk(f);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogTrace(ex, "GetFileSizeOnDisk failed for {file}, using fallback length", f.FullName);
|
||||
size = f.Length;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
size = f.Length;
|
||||
}
|
||||
|
||||
totalSizeDownscaled += size;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogTrace(ex, "Error getting size for {file}", f.FullName);
|
||||
}
|
||||
}
|
||||
|
||||
FileCacheSize = (totalSize + totalSizeDownscaled);
|
||||
}
|
||||
else
|
||||
{
|
||||
FileCacheSize = totalSize;
|
||||
}
|
||||
FileCacheSize = totalSize + totalSizeExtras;
|
||||
|
||||
var maxCacheInBytes = (long)(_configService.Current.MaxLocalCacheInGiB * 1024d * 1024d * 1024d);
|
||||
if (FileCacheSize < maxCacheInBytes)
|
||||
@@ -559,6 +519,54 @@ public sealed class CacheMonitor : DisposableMediatorSubscriberBase
|
||||
HaltScanLocks.Clear();
|
||||
}
|
||||
|
||||
private long GetFolderSize(string directory, CancellationToken token, bool isWine)
|
||||
{
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var files = Directory.EnumerateFiles(directory)
|
||||
.Select(f => new FileInfo(f))
|
||||
.OrderBy(f => f.LastAccessTime)
|
||||
.ToList();
|
||||
|
||||
long totalSize = 0;
|
||||
foreach (var file in files)
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
long size;
|
||||
if (!isWine)
|
||||
{
|
||||
try
|
||||
{
|
||||
size = _fileCompactor.GetFileSizeOnDisk(file);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogTrace(ex, "GetFileSizeOnDisk failed for {file}, using fallback length", file.FullName);
|
||||
size = file.Length;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
size = file.Length;
|
||||
}
|
||||
|
||||
totalSize += size;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogTrace(ex, "Error getting size for {file}", file.FullName);
|
||||
}
|
||||
}
|
||||
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
public void ResumeScan(string source)
|
||||
{
|
||||
if (!HaltScanLocks.ContainsKey(source)) HaltScanLocks[source] = 0;
|
||||
|
||||
@@ -582,9 +582,10 @@ public sealed class FileCacheManager : IHostedService
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveHashedFile(string hash, string prefixedFilePath)
|
||||
public void RemoveHashedFile(string hash, string prefixedFilePath, bool removeDerivedFiles = true)
|
||||
{
|
||||
var normalizedPath = NormalizePrefixedPathKey(prefixedFilePath);
|
||||
var removedHash = false;
|
||||
|
||||
if (_fileCaches.TryGetValue(hash, out var caches))
|
||||
{
|
||||
@@ -597,11 +598,16 @@ public sealed class FileCacheManager : IHostedService
|
||||
|
||||
if (caches.IsEmpty)
|
||||
{
|
||||
_fileCaches.TryRemove(hash, out _);
|
||||
removedHash = _fileCaches.TryRemove(hash, out _);
|
||||
}
|
||||
}
|
||||
|
||||
_fileCachesByPrefixedPath.TryRemove(normalizedPath, out _);
|
||||
|
||||
if (removeDerivedFiles && removedHash)
|
||||
{
|
||||
RemoveDerivedCacheFiles(hash);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateHashedFile(FileCacheEntity fileCache, bool computeProperties = true)
|
||||
@@ -617,7 +623,8 @@ public sealed class FileCacheManager : IHostedService
|
||||
fileCache.Hash = Crypto.ComputeFileHash(fileCache.ResolvedFilepath, Crypto.HashAlgo.Sha1);
|
||||
fileCache.LastModifiedDateTicks = fi.LastWriteTimeUtc.Ticks.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
RemoveHashedFile(oldHash, prefixedPath);
|
||||
var removeDerivedFiles = !string.Equals(oldHash, fileCache.Hash, StringComparison.OrdinalIgnoreCase);
|
||||
RemoveHashedFile(oldHash, prefixedPath, removeDerivedFiles);
|
||||
AddHashedFile(fileCache);
|
||||
}
|
||||
|
||||
@@ -767,7 +774,7 @@ public sealed class FileCacheManager : IHostedService
|
||||
{
|
||||
try
|
||||
{
|
||||
RemoveHashedFile(fileCache.Hash, fileCache.PrefixedFilePath);
|
||||
RemoveHashedFile(fileCache.Hash, fileCache.PrefixedFilePath, removeDerivedFiles: false);
|
||||
var extensionPath = fileCache.ResolvedFilepath.ToUpper(CultureInfo.InvariantCulture) + "." + ext;
|
||||
File.Move(fileCache.ResolvedFilepath, extensionPath, overwrite: true);
|
||||
var newHashedEntity = new FileCacheEntity(fileCache.Hash, fileCache.PrefixedFilePath + "." + ext, DateTime.UtcNow.Ticks.ToString(CultureInfo.InvariantCulture));
|
||||
@@ -784,6 +791,33 @@ public sealed class FileCacheManager : IHostedService
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveDerivedCacheFiles(string hash)
|
||||
{
|
||||
var cacheFolder = _configService.Current.CacheFolder;
|
||||
if (string.IsNullOrWhiteSpace(cacheFolder))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TryDeleteDerivedCacheFile(Path.Combine(cacheFolder, "downscaled", $"{hash}.tex"));
|
||||
TryDeleteDerivedCacheFile(Path.Combine(cacheFolder, "decimated", $"{hash}.mdl"));
|
||||
}
|
||||
|
||||
private void TryDeleteDerivedCacheFile(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogTrace(ex, "Failed to delete derived cache file {path}", path);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddHashedFile(FileCacheEntity fileCache)
|
||||
{
|
||||
var normalizedPath = NormalizePrefixedPathKey(fileCache.PrefixedFilePath);
|
||||
|
||||
@@ -22,4 +22,12 @@ public class PlayerPerformanceConfig : ILightlessConfiguration
|
||||
public int TextureDownscaleMaxDimension { get; set; } = 2048;
|
||||
public bool OnlyDownscaleUncompressedTextures { get; set; } = true;
|
||||
public bool KeepOriginalTextureFiles { get; set; } = false;
|
||||
public bool EnableModelDecimation { get; set; } = false;
|
||||
public int ModelDecimationTriangleThreshold { get; set; } = 50_000;
|
||||
public double ModelDecimationTargetRatio { get; set; } = 0.8;
|
||||
public bool ModelDecimationAllowBody { get; set; } = false;
|
||||
public bool ModelDecimationAllowFaceHead { get; set; } = false;
|
||||
public bool ModelDecimationAllowTail { get; set; } = false;
|
||||
public bool ModelDecimationAllowClothing { get; set; } = true;
|
||||
public bool ModelDecimationAllowAccessories { get; set; } = true;
|
||||
}
|
||||
@@ -5,6 +5,7 @@ namespace LightlessSync.LightlessConfiguration.Configurations;
|
||||
public class XivDataStorageConfig : ILightlessConfiguration
|
||||
{
|
||||
public ConcurrentDictionary<string, long> TriangleDictionary { get; set; } = new(StringComparer.OrdinalIgnoreCase);
|
||||
public ConcurrentDictionary<string, long> EffectiveTriangleDictionary { get; set; } = new(StringComparer.OrdinalIgnoreCase);
|
||||
public ConcurrentDictionary<string, Dictionary<string, List<ushort>>> BonesDictionary { get; set; } = new(StringComparer.OrdinalIgnoreCase);
|
||||
public int Version { get; set; } = 0;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using LightlessSync.FileCache;
|
||||
using LightlessSync.LightlessConfiguration;
|
||||
using LightlessSync.Services.Mediator;
|
||||
using LightlessSync.Services.ModelDecimation;
|
||||
using LightlessSync.Services.TextureCompression;
|
||||
using LightlessSync.WebAPI.Files;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -16,6 +17,7 @@ public class FileDownloadManagerFactory
|
||||
private readonly FileCompactor _fileCompactor;
|
||||
private readonly LightlessConfigService _configService;
|
||||
private readonly TextureDownscaleService _textureDownscaleService;
|
||||
private readonly ModelDecimationService _modelDecimationService;
|
||||
private readonly TextureMetadataHelper _textureMetadataHelper;
|
||||
|
||||
public FileDownloadManagerFactory(
|
||||
@@ -26,6 +28,7 @@ public class FileDownloadManagerFactory
|
||||
FileCompactor fileCompactor,
|
||||
LightlessConfigService configService,
|
||||
TextureDownscaleService textureDownscaleService,
|
||||
ModelDecimationService modelDecimationService,
|
||||
TextureMetadataHelper textureMetadataHelper)
|
||||
{
|
||||
_loggerFactory = loggerFactory;
|
||||
@@ -35,6 +38,7 @@ public class FileDownloadManagerFactory
|
||||
_fileCompactor = fileCompactor;
|
||||
_configService = configService;
|
||||
_textureDownscaleService = textureDownscaleService;
|
||||
_modelDecimationService = modelDecimationService;
|
||||
_textureMetadataHelper = textureMetadataHelper;
|
||||
}
|
||||
|
||||
@@ -48,6 +52,7 @@ public class FileDownloadManagerFactory
|
||||
_fileCompactor,
|
||||
_configService,
|
||||
_textureDownscaleService,
|
||||
_modelDecimationService,
|
||||
_textureMetadataHelper);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,4 +16,5 @@ public interface IPairPerformanceSubject
|
||||
long LastAppliedApproximateVRAMBytes { get; set; }
|
||||
long LastAppliedApproximateEffectiveVRAMBytes { get; set; }
|
||||
long LastAppliedDataTris { get; set; }
|
||||
long LastAppliedApproximateEffectiveTris { get; set; }
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ public class Pair
|
||||
public string? PlayerName => TryGetHandler()?.PlayerName ?? UserPair.User.AliasOrUID;
|
||||
public long LastAppliedDataBytes => TryGetHandler()?.LastAppliedDataBytes ?? -1;
|
||||
public long LastAppliedDataTris => TryGetHandler()?.LastAppliedDataTris ?? -1;
|
||||
public long LastAppliedApproximateEffectiveTris => TryGetHandler()?.LastAppliedApproximateEffectiveTris ?? -1;
|
||||
public long LastAppliedApproximateVRAMBytes => TryGetHandler()?.LastAppliedApproximateVRAMBytes ?? -1;
|
||||
public long LastAppliedApproximateEffectiveVRAMBytes => TryGetHandler()?.LastAppliedApproximateEffectiveVRAMBytes ?? -1;
|
||||
public string Ident => TryGetHandler()?.Ident ?? TryGetConnection()?.Ident ?? string.Empty;
|
||||
|
||||
@@ -12,6 +12,7 @@ using LightlessSync.Services;
|
||||
using LightlessSync.Services.ActorTracking;
|
||||
using LightlessSync.Services.Events;
|
||||
using LightlessSync.Services.Mediator;
|
||||
using LightlessSync.Services.ModelDecimation;
|
||||
using LightlessSync.Services.PairProcessing;
|
||||
using LightlessSync.Services.ServerConfiguration;
|
||||
using LightlessSync.Services.TextureCompression;
|
||||
@@ -45,6 +46,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
private readonly ServerConfigurationManager _serverConfigManager;
|
||||
private readonly PluginWarningNotificationService _pluginWarningNotificationManager;
|
||||
private readonly TextureDownscaleService _textureDownscaleService;
|
||||
private readonly ModelDecimationService _modelDecimationService;
|
||||
private readonly PairStateCache _pairStateCache;
|
||||
private readonly PairPerformanceMetricsCache _performanceMetricsCache;
|
||||
private readonly PenumbraTempCollectionJanitor _tempCollectionJanitor;
|
||||
@@ -162,6 +164,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
|
||||
public long LastAppliedDataBytes { get; private set; }
|
||||
public long LastAppliedDataTris { get; set; } = -1;
|
||||
public long LastAppliedApproximateEffectiveTris { get; set; } = -1;
|
||||
public long LastAppliedApproximateVRAMBytes { get; set; } = -1;
|
||||
public long LastAppliedApproximateEffectiveVRAMBytes { get; set; } = -1;
|
||||
public CharacterData? LastReceivedCharacterData { get; private set; }
|
||||
@@ -198,6 +201,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
PairProcessingLimiter pairProcessingLimiter,
|
||||
ServerConfigurationManager serverConfigManager,
|
||||
TextureDownscaleService textureDownscaleService,
|
||||
ModelDecimationService modelDecimationService,
|
||||
PairStateCache pairStateCache,
|
||||
PairPerformanceMetricsCache performanceMetricsCache,
|
||||
PenumbraTempCollectionJanitor tempCollectionJanitor) : base(logger, mediator)
|
||||
@@ -217,6 +221,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
_pairProcessingLimiter = pairProcessingLimiter;
|
||||
_serverConfigManager = serverConfigManager;
|
||||
_textureDownscaleService = textureDownscaleService;
|
||||
_modelDecimationService = modelDecimationService;
|
||||
_pairStateCache = pairStateCache;
|
||||
_performanceMetricsCache = performanceMetricsCache;
|
||||
_tempCollectionJanitor = tempCollectionJanitor;
|
||||
@@ -242,7 +247,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
return;
|
||||
}
|
||||
|
||||
if (LastAppliedDataBytes < 0 || LastAppliedDataTris < 0
|
||||
if (LastAppliedDataBytes < 0 || LastAppliedDataTris < 0 || LastAppliedApproximateEffectiveTris < 0
|
||||
|| LastAppliedApproximateVRAMBytes < 0 || LastAppliedApproximateEffectiveVRAMBytes < 0)
|
||||
{
|
||||
_forceApplyMods = true;
|
||||
@@ -579,6 +584,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
_forceApplyMods = true;
|
||||
LastAppliedDataBytes = -1;
|
||||
LastAppliedDataTris = -1;
|
||||
LastAppliedApproximateEffectiveTris = -1;
|
||||
LastAppliedApproximateVRAMBytes = -1;
|
||||
LastAppliedApproximateEffectiveVRAMBytes = -1;
|
||||
}
|
||||
@@ -626,6 +632,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
_forceFullReapply = true;
|
||||
LastAppliedDataBytes = -1;
|
||||
LastAppliedDataTris = -1;
|
||||
LastAppliedApproximateEffectiveTris = -1;
|
||||
LastAppliedApproximateVRAMBytes = -1;
|
||||
LastAppliedApproximateEffectiveVRAMBytes = -1;
|
||||
}
|
||||
@@ -725,6 +732,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
private void ApplyCachedMetrics(PairPerformanceMetrics metrics)
|
||||
{
|
||||
LastAppliedDataTris = metrics.TriangleCount;
|
||||
LastAppliedApproximateEffectiveTris = metrics.ApproximateEffectiveTris;
|
||||
LastAppliedApproximateVRAMBytes = metrics.ApproximateVramBytes;
|
||||
LastAppliedApproximateEffectiveVRAMBytes = metrics.ApproximateEffectiveVramBytes;
|
||||
}
|
||||
@@ -732,6 +740,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
private void StorePerformanceMetrics(CharacterData charaData)
|
||||
{
|
||||
if (LastAppliedDataTris < 0
|
||||
|| LastAppliedApproximateEffectiveTris < 0
|
||||
|| LastAppliedApproximateVRAMBytes < 0
|
||||
|| LastAppliedApproximateEffectiveVRAMBytes < 0)
|
||||
{
|
||||
@@ -747,7 +756,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
_performanceMetricsCache.StoreMetrics(
|
||||
Ident,
|
||||
dataHash,
|
||||
new PairPerformanceMetrics(LastAppliedDataTris, LastAppliedApproximateVRAMBytes, LastAppliedApproximateEffectiveVRAMBytes));
|
||||
new PairPerformanceMetrics(LastAppliedDataTris, LastAppliedApproximateVRAMBytes, LastAppliedApproximateEffectiveVRAMBytes, LastAppliedApproximateEffectiveTris));
|
||||
}
|
||||
|
||||
private bool HasMissingCachedFiles(CharacterData characterData)
|
||||
@@ -1079,7 +1088,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
Logger.LogDebug("[BASE-{appbase}] Downloading and applying character for {name}", applicationBase, GetPrimaryAliasOrUidSafe());
|
||||
|
||||
var forceFullReapply = _forceFullReapply
|
||||
|| LastAppliedApproximateVRAMBytes < 0 || LastAppliedDataTris < 0;
|
||||
|| LastAppliedApproximateVRAMBytes < 0 || LastAppliedDataTris < 0 || LastAppliedApproximateEffectiveTris < 0;
|
||||
|
||||
DownloadAndApplyCharacter(applicationBase, characterData.DeepClone(), charaDataToUpdate, forceFullReapply);
|
||||
}
|
||||
@@ -1895,6 +1904,17 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
{
|
||||
await _textureDownscaleService.WaitForPendingJobsAsync(downloadedTextureHashes, downloadToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var downloadedModelHashes = toDownloadReplacements
|
||||
.Where(static replacement => replacement.GamePaths.Any(static path => path.EndsWith(".mdl", StringComparison.OrdinalIgnoreCase)))
|
||||
.Select(static replacement => replacement.Hash)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
if (downloadedModelHashes.Count > 0)
|
||||
{
|
||||
await _modelDecimationService.WaitForPendingJobsAsync(downloadedModelHashes, downloadToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
toDownloadReplacements = TryCalculateModdedDictionary(applicationBase, charaData, out moddedPaths, downloadToken);
|
||||
@@ -2117,18 +2137,19 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
_needsCollectionRebuild = false;
|
||||
if (LastAppliedApproximateVRAMBytes < 0 || LastAppliedApproximateEffectiveVRAMBytes < 0)
|
||||
{
|
||||
_playerPerformanceService.ComputeAndAutoPauseOnVRAMUsageThresholds(this, charaData, new List<DownloadFileTransfer>());
|
||||
}
|
||||
if (LastAppliedDataTris < 0)
|
||||
{
|
||||
await _playerPerformanceService.CheckTriangleUsageThresholds(this, charaData).ConfigureAwait(false);
|
||||
}
|
||||
_playerPerformanceService.ComputeAndAutoPauseOnVRAMUsageThresholds(this, charaData, new List<DownloadFileTransfer>());
|
||||
}
|
||||
|
||||
StorePerformanceMetrics(charaData);
|
||||
_lastSuccessfulDataHash = GetDataHashSafe(charaData);
|
||||
_lastSuccessfulApplyAt = DateTime.UtcNow;
|
||||
ClearFailureState();
|
||||
Logger.LogDebug("[{applicationId}] Application finished", _applicationId);
|
||||
if (LastAppliedDataTris < 0 || LastAppliedApproximateEffectiveTris < 0)
|
||||
{
|
||||
await _playerPerformanceService.CheckTriangleUsageThresholds(this, charaData).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
StorePerformanceMetrics(charaData);
|
||||
_lastSuccessfulDataHash = GetDataHashSafe(charaData);
|
||||
_lastSuccessfulApplyAt = DateTime.UtcNow;
|
||||
ClearFailureState();
|
||||
Logger.LogDebug("[{applicationId}] Application finished", _applicationId);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
@@ -2397,9 +2418,18 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
|
||||
foreach (var gamePath in item.GamePaths)
|
||||
{
|
||||
var preferredPath = skipDownscaleForPair
|
||||
? fileCache.ResolvedFilepath
|
||||
: _textureDownscaleService.GetPreferredPath(item.Hash, fileCache.ResolvedFilepath);
|
||||
var preferredPath = fileCache.ResolvedFilepath;
|
||||
if (!skipDownscaleForPair)
|
||||
{
|
||||
if (gamePath.EndsWith(".tex", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
preferredPath = _textureDownscaleService.GetPreferredPath(item.Hash, fileCache.ResolvedFilepath);
|
||||
}
|
||||
else if (gamePath.EndsWith(".mdl", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
preferredPath = _modelDecimationService.GetPreferredPath(item.Hash, fileCache.ResolvedFilepath);
|
||||
}
|
||||
}
|
||||
outputDict[(gamePath, item.Hash)] = preferredPath;
|
||||
}
|
||||
}
|
||||
@@ -2541,6 +2571,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
_cachedData = null;
|
||||
LastAppliedDataBytes = -1;
|
||||
LastAppliedDataTris = -1;
|
||||
LastAppliedApproximateEffectiveTris = -1;
|
||||
LastAppliedApproximateVRAMBytes = -1;
|
||||
LastAppliedApproximateEffectiveVRAMBytes = -1;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using LightlessSync.PlayerData.Factories;
|
||||
using LightlessSync.Services;
|
||||
using LightlessSync.Services.ActorTracking;
|
||||
using LightlessSync.Services.Mediator;
|
||||
using LightlessSync.Services.ModelDecimation;
|
||||
using LightlessSync.Services.PairProcessing;
|
||||
using LightlessSync.Services.ServerConfiguration;
|
||||
using LightlessSync.Services.TextureCompression;
|
||||
@@ -30,6 +31,7 @@ internal sealed class PairHandlerAdapterFactory : IPairHandlerAdapterFactory
|
||||
private readonly PairProcessingLimiter _pairProcessingLimiter;
|
||||
private readonly ServerConfigurationManager _serverConfigManager;
|
||||
private readonly TextureDownscaleService _textureDownscaleService;
|
||||
private readonly ModelDecimationService _modelDecimationService;
|
||||
private readonly PairStateCache _pairStateCache;
|
||||
private readonly PairPerformanceMetricsCache _pairPerformanceMetricsCache;
|
||||
private readonly PenumbraTempCollectionJanitor _tempCollectionJanitor;
|
||||
@@ -51,6 +53,7 @@ internal sealed class PairHandlerAdapterFactory : IPairHandlerAdapterFactory
|
||||
PairProcessingLimiter pairProcessingLimiter,
|
||||
ServerConfigurationManager serverConfigManager,
|
||||
TextureDownscaleService textureDownscaleService,
|
||||
ModelDecimationService modelDecimationService,
|
||||
PairStateCache pairStateCache,
|
||||
PairPerformanceMetricsCache pairPerformanceMetricsCache,
|
||||
PenumbraTempCollectionJanitor tempCollectionJanitor)
|
||||
@@ -70,6 +73,7 @@ internal sealed class PairHandlerAdapterFactory : IPairHandlerAdapterFactory
|
||||
_pairProcessingLimiter = pairProcessingLimiter;
|
||||
_serverConfigManager = serverConfigManager;
|
||||
_textureDownscaleService = textureDownscaleService;
|
||||
_modelDecimationService = modelDecimationService;
|
||||
_pairStateCache = pairStateCache;
|
||||
_pairPerformanceMetricsCache = pairPerformanceMetricsCache;
|
||||
_tempCollectionJanitor = tempCollectionJanitor;
|
||||
@@ -98,6 +102,7 @@ internal sealed class PairHandlerAdapterFactory : IPairHandlerAdapterFactory
|
||||
_pairProcessingLimiter,
|
||||
_serverConfigManager,
|
||||
_textureDownscaleService,
|
||||
_modelDecimationService,
|
||||
_pairStateCache,
|
||||
_pairPerformanceMetricsCache,
|
||||
_tempCollectionJanitor);
|
||||
|
||||
@@ -89,7 +89,7 @@ public sealed class PairHandlerRegistry : IDisposable
|
||||
}
|
||||
|
||||
if (handler.LastReceivedCharacterData is not null &&
|
||||
(handler.LastAppliedApproximateVRAMBytes < 0 || handler.LastAppliedDataTris < 0))
|
||||
(handler.LastAppliedApproximateVRAMBytes < 0 || handler.LastAppliedDataTris < 0 || handler.LastAppliedApproximateEffectiveTris < 0))
|
||||
{
|
||||
handler.ApplyLastReceivedData(forced: true);
|
||||
}
|
||||
|
||||
@@ -258,7 +258,8 @@ public sealed class PairLedger : DisposableMediatorSubscriberBase
|
||||
|
||||
if (handler.LastAppliedApproximateVRAMBytes >= 0
|
||||
&& handler.LastAppliedDataTris >= 0
|
||||
&& handler.LastAppliedApproximateEffectiveVRAMBytes >= 0)
|
||||
&& handler.LastAppliedApproximateEffectiveVRAMBytes >= 0
|
||||
&& handler.LastAppliedApproximateEffectiveTris >= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ namespace LightlessSync.PlayerData.Pairs;
|
||||
public readonly record struct PairPerformanceMetrics(
|
||||
long TriangleCount,
|
||||
long ApproximateVramBytes,
|
||||
long ApproximateEffectiveVramBytes);
|
||||
long ApproximateEffectiveVramBytes,
|
||||
long ApproximateEffectiveTris);
|
||||
|
||||
/// <summary>
|
||||
/// caches performance metrics keyed by pair ident
|
||||
|
||||
@@ -40,6 +40,7 @@ using System.Reflection;
|
||||
using OtterTex;
|
||||
using LightlessSync.Services.LightFinder;
|
||||
using LightlessSync.Services.PairProcessing;
|
||||
using LightlessSync.Services.ModelDecimation;
|
||||
using LightlessSync.UI.Models;
|
||||
|
||||
namespace LightlessSync;
|
||||
@@ -126,6 +127,7 @@ public sealed class Plugin : IDalamudPlugin
|
||||
services.AddSingleton<LightlessProfileManager>();
|
||||
services.AddSingleton<TextureCompressionService>();
|
||||
services.AddSingleton<TextureDownscaleService>();
|
||||
services.AddSingleton<ModelDecimationService>();
|
||||
services.AddSingleton<GameObjectHandlerFactory>();
|
||||
services.AddSingleton<FileDownloadManagerFactory>();
|
||||
services.AddSingleton<PairProcessingLimiter>();
|
||||
|
||||
1439
LightlessSync/Services/ModelDecimation/MdlDecimator.cs
Normal file
1439
LightlessSync/Services/ModelDecimation/MdlDecimator.cs
Normal file
File diff suppressed because it is too large
Load Diff
275
LightlessSync/Services/ModelDecimation/ModelDecimationService.cs
Normal file
275
LightlessSync/Services/ModelDecimation/ModelDecimationService.cs
Normal file
@@ -0,0 +1,275 @@
|
||||
using LightlessSync.LightlessConfiguration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace LightlessSync.Services.ModelDecimation;
|
||||
|
||||
public sealed class ModelDecimationService
|
||||
{
|
||||
private const int MaxConcurrentJobs = 1;
|
||||
private const double MinTargetRatio = 0.01;
|
||||
private const double MaxTargetRatio = 0.99;
|
||||
|
||||
private readonly ILogger<ModelDecimationService> _logger;
|
||||
private readonly LightlessConfigService _configService;
|
||||
private readonly PlayerPerformanceConfigService _performanceConfigService;
|
||||
private readonly XivDataStorageService _xivDataStorageService;
|
||||
private readonly SemaphoreSlim _decimationSemaphore = new(MaxConcurrentJobs);
|
||||
|
||||
private readonly ConcurrentDictionary<string, Task> _activeJobs = new(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly ConcurrentDictionary<string, string> _decimatedPaths = new(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly ConcurrentDictionary<string, byte> _failedHashes = new(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public ModelDecimationService(
|
||||
ILogger<ModelDecimationService> logger,
|
||||
LightlessConfigService configService,
|
||||
PlayerPerformanceConfigService performanceConfigService,
|
||||
XivDataStorageService xivDataStorageService)
|
||||
{
|
||||
_logger = logger;
|
||||
_configService = configService;
|
||||
_performanceConfigService = performanceConfigService;
|
||||
_xivDataStorageService = xivDataStorageService;
|
||||
}
|
||||
|
||||
public void ScheduleDecimation(string hash, string filePath, string? gamePath = null)
|
||||
{
|
||||
if (!ShouldScheduleDecimation(hash, filePath, gamePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_decimatedPaths.ContainsKey(hash) || _failedHashes.ContainsKey(hash) || _activeJobs.ContainsKey(hash))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogInformation("Queued model decimation for {Hash}", hash);
|
||||
|
||||
_activeJobs[hash] = Task.Run(async () =>
|
||||
{
|
||||
await _decimationSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await DecimateInternalAsync(hash, filePath).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_failedHashes[hash] = 1;
|
||||
_logger.LogWarning(ex, "Model decimation failed for {Hash}", hash);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_decimationSemaphore.Release();
|
||||
_activeJobs.TryRemove(hash, out _);
|
||||
}
|
||||
}, CancellationToken.None);
|
||||
}
|
||||
|
||||
public bool ShouldScheduleDecimation(string hash, string filePath, string? gamePath = null)
|
||||
=> IsDecimationEnabled()
|
||||
&& filePath.EndsWith(".mdl", StringComparison.OrdinalIgnoreCase)
|
||||
&& IsDecimationAllowed(gamePath)
|
||||
&& !ShouldSkipByTriangleCache(hash);
|
||||
|
||||
public string GetPreferredPath(string hash, string originalPath)
|
||||
{
|
||||
if (!IsDecimationEnabled())
|
||||
{
|
||||
return originalPath;
|
||||
}
|
||||
|
||||
if (_decimatedPaths.TryGetValue(hash, out var existing) && File.Exists(existing))
|
||||
{
|
||||
return existing;
|
||||
}
|
||||
|
||||
var resolved = GetExistingDecimatedPath(hash);
|
||||
if (!string.IsNullOrEmpty(resolved))
|
||||
{
|
||||
_decimatedPaths[hash] = resolved;
|
||||
return resolved;
|
||||
}
|
||||
|
||||
return originalPath;
|
||||
}
|
||||
|
||||
public Task WaitForPendingJobsAsync(IEnumerable<string>? hashes, CancellationToken token)
|
||||
{
|
||||
if (hashes is null)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
var pending = new List<Task>();
|
||||
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var hash in hashes)
|
||||
{
|
||||
if (string.IsNullOrEmpty(hash) || !seen.Add(hash))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_activeJobs.TryGetValue(hash, out var job))
|
||||
{
|
||||
pending.Add(job);
|
||||
}
|
||||
}
|
||||
|
||||
if (pending.Count == 0)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
return Task.WhenAll(pending).WaitAsync(token);
|
||||
}
|
||||
|
||||
private Task DecimateInternalAsync(string hash, string sourcePath)
|
||||
{
|
||||
if (!File.Exists(sourcePath))
|
||||
{
|
||||
_failedHashes[hash] = 1;
|
||||
_logger.LogWarning("Cannot decimate model {Hash}; source path missing: {Path}", hash, sourcePath);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
if (!TryGetDecimationSettings(out var triangleThreshold, out var targetRatio))
|
||||
{
|
||||
_logger.LogInformation("Model decimation disabled or invalid settings for {Hash}", hash);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
_logger.LogInformation("Starting model decimation for {Hash} (threshold {Threshold}, ratio {Ratio:0.##})", hash, triangleThreshold, targetRatio);
|
||||
|
||||
var destination = Path.Combine(GetDecimatedDirectory(), $"{hash}.mdl");
|
||||
if (File.Exists(destination))
|
||||
{
|
||||
_decimatedPaths[hash] = destination;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
if (!MdlDecimator.TryDecimate(sourcePath, destination, triangleThreshold, targetRatio, _logger))
|
||||
{
|
||||
_failedHashes[hash] = 1;
|
||||
_logger.LogInformation("Model decimation skipped for {Hash}", hash);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
_decimatedPaths[hash] = destination;
|
||||
_logger.LogInformation("Decimated model {Hash} -> {Path}", hash, destination);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private bool IsDecimationEnabled()
|
||||
=> _performanceConfigService.Current.EnableModelDecimation;
|
||||
|
||||
private bool ShouldSkipByTriangleCache(string hash)
|
||||
{
|
||||
if (string.IsNullOrEmpty(hash))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_xivDataStorageService.Current.TriangleDictionary.TryGetValue(hash, out var cachedTris) || cachedTris <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var threshold = Math.Max(0, _performanceConfigService.Current.ModelDecimationTriangleThreshold);
|
||||
return threshold > 0 && cachedTris < threshold;
|
||||
}
|
||||
|
||||
private bool IsDecimationAllowed(string? gamePath)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(gamePath))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var normalized = NormalizeGamePath(gamePath);
|
||||
if (normalized.Contains("/hair/", StringComparison.Ordinal))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (normalized.Contains("/chara/equipment/", StringComparison.Ordinal))
|
||||
{
|
||||
return _performanceConfigService.Current.ModelDecimationAllowClothing;
|
||||
}
|
||||
|
||||
if (normalized.Contains("/chara/accessory/", StringComparison.Ordinal))
|
||||
{
|
||||
return _performanceConfigService.Current.ModelDecimationAllowAccessories;
|
||||
}
|
||||
|
||||
if (normalized.Contains("/chara/human/", StringComparison.Ordinal))
|
||||
{
|
||||
if (normalized.Contains("/body/", StringComparison.Ordinal))
|
||||
{
|
||||
return _performanceConfigService.Current.ModelDecimationAllowBody;
|
||||
}
|
||||
|
||||
if (normalized.Contains("/face/", StringComparison.Ordinal) || normalized.Contains("/head/", StringComparison.Ordinal))
|
||||
{
|
||||
return _performanceConfigService.Current.ModelDecimationAllowFaceHead;
|
||||
}
|
||||
|
||||
if (normalized.Contains("/tail/", StringComparison.Ordinal))
|
||||
{
|
||||
return _performanceConfigService.Current.ModelDecimationAllowTail;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string NormalizeGamePath(string path)
|
||||
=> path.Replace('\\', '/').ToLowerInvariant();
|
||||
|
||||
private bool TryGetDecimationSettings(out int triangleThreshold, out double targetRatio)
|
||||
{
|
||||
triangleThreshold = 15_000;
|
||||
targetRatio = 0.8;
|
||||
|
||||
var config = _performanceConfigService.Current;
|
||||
if (!config.EnableModelDecimation)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
triangleThreshold = Math.Max(0, config.ModelDecimationTriangleThreshold);
|
||||
targetRatio = config.ModelDecimationTargetRatio;
|
||||
if (double.IsNaN(targetRatio) || double.IsInfinity(targetRatio))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
targetRatio = Math.Clamp(targetRatio, MinTargetRatio, MaxTargetRatio);
|
||||
return true;
|
||||
}
|
||||
|
||||
private string? GetExistingDecimatedPath(string hash)
|
||||
{
|
||||
var candidate = Path.Combine(GetDecimatedDirectory(), $"{hash}.mdl");
|
||||
return File.Exists(candidate) ? candidate : null;
|
||||
}
|
||||
|
||||
private string GetDecimatedDirectory()
|
||||
{
|
||||
var directory = Path.Combine(_configService.Current.CacheFolder, "decimated");
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogTrace(ex, "Failed to create decimated directory {Directory}", directory);
|
||||
}
|
||||
}
|
||||
|
||||
return directory;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using LightlessSync.LightlessConfiguration;
|
||||
using LightlessSync.PlayerData.Pairs;
|
||||
using LightlessSync.Services.Events;
|
||||
using LightlessSync.Services.Mediator;
|
||||
using LightlessSync.Services.ModelDecimation;
|
||||
using LightlessSync.Services.TextureCompression;
|
||||
using LightlessSync.UI;
|
||||
using LightlessSync.WebAPI.Files.Models;
|
||||
@@ -18,12 +19,14 @@ public class PlayerPerformanceService
|
||||
private readonly ILogger<PlayerPerformanceService> _logger;
|
||||
private readonly LightlessMediator _mediator;
|
||||
private readonly PlayerPerformanceConfigService _playerPerformanceConfigService;
|
||||
private readonly ModelDecimationService _modelDecimationService;
|
||||
private readonly TextureDownscaleService _textureDownscaleService;
|
||||
private readonly Dictionary<string, bool> _warnedForPlayers = new(StringComparer.Ordinal);
|
||||
|
||||
public PlayerPerformanceService(ILogger<PlayerPerformanceService> logger, LightlessMediator mediator,
|
||||
PlayerPerformanceConfigService playerPerformanceConfigService, FileCacheManager fileCacheManager,
|
||||
XivDataAnalyzer xivDataAnalyzer, TextureDownscaleService textureDownscaleService)
|
||||
XivDataAnalyzer xivDataAnalyzer, TextureDownscaleService textureDownscaleService,
|
||||
ModelDecimationService modelDecimationService)
|
||||
{
|
||||
_logger = logger;
|
||||
_mediator = mediator;
|
||||
@@ -31,6 +34,7 @@ public class PlayerPerformanceService
|
||||
_fileCacheManager = fileCacheManager;
|
||||
_xivDataAnalyzer = xivDataAnalyzer;
|
||||
_textureDownscaleService = textureDownscaleService;
|
||||
_modelDecimationService = modelDecimationService;
|
||||
}
|
||||
|
||||
public async Task<bool> CheckBothThresholds(IPairPerformanceSubject pairHandler, CharacterData charaData)
|
||||
@@ -111,10 +115,12 @@ public class PlayerPerformanceService
|
||||
var config = _playerPerformanceConfigService.Current;
|
||||
|
||||
long triUsage = 0;
|
||||
long effectiveTriUsage = 0;
|
||||
|
||||
if (!charaData.FileReplacements.TryGetValue(API.Data.Enum.ObjectKind.Player, out List<FileReplacementData>? playerReplacements))
|
||||
{
|
||||
pairHandler.LastAppliedDataTris = 0;
|
||||
pairHandler.LastAppliedApproximateEffectiveTris = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -125,12 +131,31 @@ public class PlayerPerformanceService
|
||||
|
||||
foreach (var hash in moddedModelHashes)
|
||||
{
|
||||
triUsage += await _xivDataAnalyzer.GetTrianglesByHash(hash).ConfigureAwait(false);
|
||||
var tris = await _xivDataAnalyzer.GetTrianglesByHash(hash).ConfigureAwait(false);
|
||||
triUsage += tris;
|
||||
|
||||
long effectiveTris = tris;
|
||||
var fileEntry = _fileCacheManager.GetFileCacheByHash(hash);
|
||||
if (fileEntry != null)
|
||||
{
|
||||
var preferredPath = _modelDecimationService.GetPreferredPath(hash, fileEntry.ResolvedFilepath);
|
||||
if (!string.Equals(preferredPath, fileEntry.ResolvedFilepath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var decimatedTris = await _xivDataAnalyzer.GetEffectiveTrianglesByHash(hash, preferredPath).ConfigureAwait(false);
|
||||
if (decimatedTris > 0)
|
||||
{
|
||||
effectiveTris = decimatedTris;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
effectiveTriUsage += effectiveTris;
|
||||
}
|
||||
|
||||
pairHandler.LastAppliedDataTris = triUsage;
|
||||
pairHandler.LastAppliedApproximateEffectiveTris = effectiveTriUsage;
|
||||
|
||||
_logger.LogDebug("Calculated VRAM usage for {p}", pairHandler);
|
||||
_logger.LogDebug("Calculated triangle usage for {p}", pairHandler);
|
||||
|
||||
// no warning of any kind on ignored pairs
|
||||
if (config.UIDsToIgnore
|
||||
@@ -274,4 +299,4 @@ public class PlayerPerformanceService
|
||||
|
||||
private static bool CheckForThreshold(bool thresholdEnabled, long threshold, long value, bool checkForPrefPerm, bool isPrefPerm) =>
|
||||
thresholdEnabled && threshold > 0 && threshold < value && ((checkForPrefPerm && isPrefPerm) || !isPrefPerm);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -678,7 +678,7 @@ public sealed class TextureDownscaleService
|
||||
|
||||
if (!string.Equals(cacheEntry.PrefixedFilePath, prefixed, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_fileCacheManager.RemoveHashedFile(cacheEntry.Hash, cacheEntry.PrefixedFilePath);
|
||||
_fileCacheManager.RemoveHashedFile(cacheEntry.Hash, cacheEntry.PrefixedFilePath, removeDerivedFiles: false);
|
||||
}
|
||||
|
||||
_fileCacheManager.UpdateHashedFile(replacement, computeProperties: false);
|
||||
|
||||
@@ -8,6 +8,7 @@ using LightlessSync.Interop.GameModel;
|
||||
using LightlessSync.LightlessConfiguration;
|
||||
using LightlessSync.PlayerData.Handlers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace LightlessSync.Services;
|
||||
@@ -18,6 +19,7 @@ public sealed class XivDataAnalyzer
|
||||
private readonly FileCacheManager _fileCacheManager;
|
||||
private readonly XivDataStorageService _configService;
|
||||
private readonly List<string> _failedCalculatedTris = [];
|
||||
private readonly List<string> _failedCalculatedEffectiveTris = [];
|
||||
|
||||
public XivDataAnalyzer(ILogger<XivDataAnalyzer> logger, FileCacheManager fileCacheManager,
|
||||
XivDataStorageService configService)
|
||||
@@ -162,16 +164,41 @@ public sealed class XivDataAnalyzer
|
||||
if (path == null || !path.ResolvedFilepath.EndsWith(".mdl", StringComparison.OrdinalIgnoreCase))
|
||||
return 0;
|
||||
|
||||
var filePath = path.ResolvedFilepath;
|
||||
return CalculateTrianglesFromPath(hash, path.ResolvedFilepath, _configService.Current.TriangleDictionary, _failedCalculatedTris);
|
||||
}
|
||||
|
||||
public async Task<long> GetEffectiveTrianglesByHash(string hash, string filePath)
|
||||
{
|
||||
if (_configService.Current.EffectiveTriangleDictionary.TryGetValue(hash, out var cachedTris) && cachedTris > 0)
|
||||
return cachedTris;
|
||||
|
||||
if (_failedCalculatedEffectiveTris.Contains(hash, StringComparer.Ordinal))
|
||||
return 0;
|
||||
|
||||
if (string.IsNullOrEmpty(filePath)
|
||||
|| !filePath.EndsWith(".mdl", StringComparison.OrdinalIgnoreCase)
|
||||
|| !File.Exists(filePath))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return CalculateTrianglesFromPath(hash, filePath, _configService.Current.EffectiveTriangleDictionary, _failedCalculatedEffectiveTris);
|
||||
}
|
||||
|
||||
private long CalculateTrianglesFromPath(
|
||||
string hash,
|
||||
string filePath,
|
||||
ConcurrentDictionary<string, long> cache,
|
||||
List<string> failedList)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogDebug("Detected Model File {path}, calculating Tris", filePath);
|
||||
var file = new MdlFile(filePath);
|
||||
if (file.LodCount <= 0)
|
||||
{
|
||||
_failedCalculatedTris.Add(hash);
|
||||
_configService.Current.TriangleDictionary[hash] = 0;
|
||||
failedList.Add(hash);
|
||||
cache[hash] = 0;
|
||||
_configService.Save();
|
||||
return 0;
|
||||
}
|
||||
@@ -195,7 +222,7 @@ public sealed class XivDataAnalyzer
|
||||
if (tris > 0)
|
||||
{
|
||||
_logger.LogDebug("TriAnalysis: {filePath} => {tris} triangles", filePath, tris);
|
||||
_configService.Current.TriangleDictionary[hash] = tris;
|
||||
cache[hash] = tris;
|
||||
_configService.Save();
|
||||
break;
|
||||
}
|
||||
@@ -205,8 +232,8 @@ public sealed class XivDataAnalyzer
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_failedCalculatedTris.Add(hash);
|
||||
_configService.Current.TriangleDictionary[hash] = 0;
|
||||
failedList.Add(hash);
|
||||
cache[hash] = 0;
|
||||
_configService.Save();
|
||||
_logger.LogWarning(e, "Could not parse file {file}", filePath);
|
||||
return 0;
|
||||
|
||||
169
LightlessSync/ThirdParty/MeshDecimator/Algorithms/DecimationAlgorithm.cs
vendored
Normal file
169
LightlessSync/ThirdParty/MeshDecimator/Algorithms/DecimationAlgorithm.cs
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
#region License
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright(c) 2017-2018 Mattias Edlund
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MeshDecimator.Algorithms
|
||||
{
|
||||
/// <summary>
|
||||
/// A decimation algorithm.
|
||||
/// </summary>
|
||||
public abstract class DecimationAlgorithm
|
||||
{
|
||||
#region Delegates
|
||||
/// <summary>
|
||||
/// A callback for decimation status reports.
|
||||
/// </summary>
|
||||
/// <param name="iteration">The current iteration, starting at zero.</param>
|
||||
/// <param name="originalTris">The original count of triangles.</param>
|
||||
/// <param name="currentTris">The current count of triangles.</param>
|
||||
/// <param name="targetTris">The target count of triangles.</param>
|
||||
public delegate void StatusReportCallback(int iteration, int originalTris, int currentTris, int targetTris);
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private bool preserveBorders = false;
|
||||
private int maxVertexCount = 0;
|
||||
private bool verbose = false;
|
||||
|
||||
private StatusReportCallback statusReportInvoker = null;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Gets or sets if borders should be kept.
|
||||
/// Default value: false
|
||||
/// </summary>
|
||||
[Obsolete("Use the 'DecimationAlgorithm.PreserveBorders' property instead.", false)]
|
||||
public bool KeepBorders
|
||||
{
|
||||
get { return preserveBorders; }
|
||||
set { preserveBorders = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if borders should be preserved.
|
||||
/// Default value: false
|
||||
/// </summary>
|
||||
public bool PreserveBorders
|
||||
{
|
||||
get { return preserveBorders; }
|
||||
set { preserveBorders = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if linked vertices should be kept.
|
||||
/// Default value: false
|
||||
/// </summary>
|
||||
[Obsolete("This feature has been removed, for more details why please read the readme.", true)]
|
||||
public bool KeepLinkedVertices
|
||||
{
|
||||
get { return false; }
|
||||
set { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum vertex count. Set to zero for no limitation.
|
||||
/// Default value: 0 (no limitation)
|
||||
/// </summary>
|
||||
public int MaxVertexCount
|
||||
{
|
||||
get { return maxVertexCount; }
|
||||
set { maxVertexCount = Math.MathHelper.Max(value, 0); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if verbose information should be printed in the console.
|
||||
/// Default value: false
|
||||
/// </summary>
|
||||
public bool Verbose
|
||||
{
|
||||
get { return verbose; }
|
||||
set { verbose = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the logger used for diagnostics.
|
||||
/// </summary>
|
||||
public ILogger? Logger { get; set; }
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
/// <summary>
|
||||
/// An event for status reports for this algorithm.
|
||||
/// </summary>
|
||||
public event StatusReportCallback StatusReport
|
||||
{
|
||||
add { statusReportInvoker += value; }
|
||||
remove { statusReportInvoker -= value; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Protected Methods
|
||||
/// <summary>
|
||||
/// Reports the current status of the decimation.
|
||||
/// </summary>
|
||||
/// <param name="iteration">The current iteration, starting at zero.</param>
|
||||
/// <param name="originalTris">The original count of triangles.</param>
|
||||
/// <param name="currentTris">The current count of triangles.</param>
|
||||
/// <param name="targetTris">The target count of triangles.</param>
|
||||
protected void ReportStatus(int iteration, int originalTris, int currentTris, int targetTris)
|
||||
{
|
||||
var statusReportInvoker = this.statusReportInvoker;
|
||||
if (statusReportInvoker != null)
|
||||
{
|
||||
statusReportInvoker.Invoke(iteration, originalTris, currentTris, targetTris);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
/// <summary>
|
||||
/// Initializes the algorithm with the original mesh.
|
||||
/// </summary>
|
||||
/// <param name="mesh">The mesh.</param>
|
||||
public abstract void Initialize(Mesh mesh);
|
||||
|
||||
/// <summary>
|
||||
/// Decimates the mesh.
|
||||
/// </summary>
|
||||
/// <param name="targetTrisCount">The target triangle count.</param>
|
||||
public abstract void DecimateMesh(int targetTrisCount);
|
||||
|
||||
/// <summary>
|
||||
/// Decimates the mesh without losing any quality.
|
||||
/// </summary>
|
||||
public abstract void DecimateMeshLossless();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the resulting mesh.
|
||||
/// </summary>
|
||||
/// <returns>The resulting mesh.</returns>
|
||||
public abstract Mesh ToMesh();
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
1549
LightlessSync/ThirdParty/MeshDecimator/Algorithms/FastQuadricMeshSimplification.cs
vendored
Normal file
1549
LightlessSync/ThirdParty/MeshDecimator/Algorithms/FastQuadricMeshSimplification.cs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
249
LightlessSync/ThirdParty/MeshDecimator/BoneWeight.cs
vendored
Normal file
249
LightlessSync/ThirdParty/MeshDecimator/BoneWeight.cs
vendored
Normal file
@@ -0,0 +1,249 @@
|
||||
#region License
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright(c) 2017-2018 Mattias Edlund
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using MeshDecimator.Math;
|
||||
|
||||
namespace MeshDecimator
|
||||
{
|
||||
/// <summary>
|
||||
/// A bone weight.
|
||||
/// </summary>
|
||||
public struct BoneWeight : IEquatable<BoneWeight>
|
||||
{
|
||||
#region Fields
|
||||
/// <summary>
|
||||
/// The first bone index.
|
||||
/// </summary>
|
||||
public int boneIndex0;
|
||||
/// <summary>
|
||||
/// The second bone index.
|
||||
/// </summary>
|
||||
public int boneIndex1;
|
||||
/// <summary>
|
||||
/// The third bone index.
|
||||
/// </summary>
|
||||
public int boneIndex2;
|
||||
/// <summary>
|
||||
/// The fourth bone index.
|
||||
/// </summary>
|
||||
public int boneIndex3;
|
||||
|
||||
/// <summary>
|
||||
/// The first bone weight.
|
||||
/// </summary>
|
||||
public float boneWeight0;
|
||||
/// <summary>
|
||||
/// The second bone weight.
|
||||
/// </summary>
|
||||
public float boneWeight1;
|
||||
/// <summary>
|
||||
/// The third bone weight.
|
||||
/// </summary>
|
||||
public float boneWeight2;
|
||||
/// <summary>
|
||||
/// The fourth bone weight.
|
||||
/// </summary>
|
||||
public float boneWeight3;
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Creates a new bone weight.
|
||||
/// </summary>
|
||||
/// <param name="boneIndex0">The first bone index.</param>
|
||||
/// <param name="boneIndex1">The second bone index.</param>
|
||||
/// <param name="boneIndex2">The third bone index.</param>
|
||||
/// <param name="boneIndex3">The fourth bone index.</param>
|
||||
/// <param name="boneWeight0">The first bone weight.</param>
|
||||
/// <param name="boneWeight1">The second bone weight.</param>
|
||||
/// <param name="boneWeight2">The third bone weight.</param>
|
||||
/// <param name="boneWeight3">The fourth bone weight.</param>
|
||||
public BoneWeight(int boneIndex0, int boneIndex1, int boneIndex2, int boneIndex3, float boneWeight0, float boneWeight1, float boneWeight2, float boneWeight3)
|
||||
{
|
||||
this.boneIndex0 = boneIndex0;
|
||||
this.boneIndex1 = boneIndex1;
|
||||
this.boneIndex2 = boneIndex2;
|
||||
this.boneIndex3 = boneIndex3;
|
||||
|
||||
this.boneWeight0 = boneWeight0;
|
||||
this.boneWeight1 = boneWeight1;
|
||||
this.boneWeight2 = boneWeight2;
|
||||
this.boneWeight3 = boneWeight3;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
/// <summary>
|
||||
/// Returns if two bone weights equals eachother.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side bone weight.</param>
|
||||
/// <param name="rhs">The right hand side bone weight.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public static bool operator ==(BoneWeight lhs, BoneWeight rhs)
|
||||
{
|
||||
return (lhs.boneIndex0 == rhs.boneIndex0 && lhs.boneIndex1 == rhs.boneIndex1 && lhs.boneIndex2 == rhs.boneIndex2 && lhs.boneIndex3 == rhs.boneIndex3 &&
|
||||
new Vector4(lhs.boneWeight0, lhs.boneWeight1, lhs.boneWeight2, lhs.boneWeight3) == new Vector4(rhs.boneWeight0, rhs.boneWeight1, rhs.boneWeight2, rhs.boneWeight3));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if two bone weights don't equal eachother.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side bone weight.</param>
|
||||
/// <param name="rhs">The right hand side bone weight.</param>
|
||||
/// <returns>If not equals.</returns>
|
||||
public static bool operator !=(BoneWeight lhs, BoneWeight rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
private void MergeBoneWeight(int boneIndex, float weight)
|
||||
{
|
||||
if (boneIndex == boneIndex0)
|
||||
{
|
||||
boneWeight0 = (boneWeight0 + weight) * 0.5f;
|
||||
}
|
||||
else if (boneIndex == boneIndex1)
|
||||
{
|
||||
boneWeight1 = (boneWeight1 + weight) * 0.5f;
|
||||
}
|
||||
else if (boneIndex == boneIndex2)
|
||||
{
|
||||
boneWeight2 = (boneWeight2 + weight) * 0.5f;
|
||||
}
|
||||
else if (boneIndex == boneIndex3)
|
||||
{
|
||||
boneWeight3 = (boneWeight3 + weight) * 0.5f;
|
||||
}
|
||||
else if(boneWeight0 == 0f)
|
||||
{
|
||||
boneIndex0 = boneIndex;
|
||||
boneWeight0 = weight;
|
||||
}
|
||||
else if (boneWeight1 == 0f)
|
||||
{
|
||||
boneIndex1 = boneIndex;
|
||||
boneWeight1 = weight;
|
||||
}
|
||||
else if (boneWeight2 == 0f)
|
||||
{
|
||||
boneIndex2 = boneIndex;
|
||||
boneWeight2 = weight;
|
||||
}
|
||||
else if (boneWeight3 == 0f)
|
||||
{
|
||||
boneIndex3 = boneIndex;
|
||||
boneWeight3 = weight;
|
||||
}
|
||||
Normalize();
|
||||
}
|
||||
|
||||
private void Normalize()
|
||||
{
|
||||
float mag = (float)System.Math.Sqrt(boneWeight0 * boneWeight0 + boneWeight1 * boneWeight1 + boneWeight2 * boneWeight2 + boneWeight3 * boneWeight3);
|
||||
if (mag > float.Epsilon)
|
||||
{
|
||||
boneWeight0 /= mag;
|
||||
boneWeight1 /= mag;
|
||||
boneWeight2 /= mag;
|
||||
boneWeight3 /= mag;
|
||||
}
|
||||
else
|
||||
{
|
||||
boneWeight0 = boneWeight1 = boneWeight2 = boneWeight3 = 0f;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
#region Object
|
||||
/// <summary>
|
||||
/// Returns a hash code for this vector.
|
||||
/// </summary>
|
||||
/// <returns>The hash code.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return boneIndex0.GetHashCode() ^ boneIndex1.GetHashCode() << 2 ^ boneIndex2.GetHashCode() >> 2 ^ boneIndex3.GetHashCode() >>
|
||||
1 ^ boneWeight0.GetHashCode() << 5 ^ boneWeight1.GetHashCode() << 4 ^ boneWeight2.GetHashCode() >> 4 ^ boneWeight3.GetHashCode() >> 3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if this bone weight is equal to another object.
|
||||
/// </summary>
|
||||
/// <param name="obj">The other object to compare to.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (!(obj is BoneWeight))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
BoneWeight other = (BoneWeight)obj;
|
||||
return (boneIndex0 == other.boneIndex0 && boneIndex1 == other.boneIndex1 && boneIndex2 == other.boneIndex2 && boneIndex3 == other.boneIndex3 &&
|
||||
boneWeight0 == other.boneWeight0 && boneWeight1 == other.boneWeight1 && boneWeight2 == other.boneWeight2 && boneWeight3 == other.boneWeight3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if this bone weight is equal to another one.
|
||||
/// </summary>
|
||||
/// <param name="other">The other bone weight to compare to.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public bool Equals(BoneWeight other)
|
||||
{
|
||||
return (boneIndex0 == other.boneIndex0 && boneIndex1 == other.boneIndex1 && boneIndex2 == other.boneIndex2 && boneIndex3 == other.boneIndex3 &&
|
||||
boneWeight0 == other.boneWeight0 && boneWeight1 == other.boneWeight1 && boneWeight2 == other.boneWeight2 && boneWeight3 == other.boneWeight3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a nicely formatted string for this bone weight.
|
||||
/// </summary>
|
||||
/// <returns>The string.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("({0}:{4:F1}, {1}:{5:F1}, {2}:{6:F1}, {3}:{7:F1})",
|
||||
boneIndex0, boneIndex1, boneIndex2, boneIndex3, boneWeight0, boneWeight1, boneWeight2, boneWeight3);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Static
|
||||
/// <summary>
|
||||
/// Merges two bone weights and stores the merged result in the first parameter.
|
||||
/// </summary>
|
||||
/// <param name="a">The first bone weight, also stores result.</param>
|
||||
/// <param name="b">The second bone weight.</param>
|
||||
public static void Merge(ref BoneWeight a, ref BoneWeight b)
|
||||
{
|
||||
if (b.boneWeight0 > 0f) a.MergeBoneWeight(b.boneIndex0, b.boneWeight0);
|
||||
if (b.boneWeight1 > 0f) a.MergeBoneWeight(b.boneIndex1, b.boneWeight1);
|
||||
if (b.boneWeight2 > 0f) a.MergeBoneWeight(b.boneIndex2, b.boneWeight2);
|
||||
if (b.boneWeight3 > 0f) a.MergeBoneWeight(b.boneIndex3, b.boneWeight3);
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
179
LightlessSync/ThirdParty/MeshDecimator/Collections/ResizableArray.cs
vendored
Normal file
179
LightlessSync/ThirdParty/MeshDecimator/Collections/ResizableArray.cs
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
#region License
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright(c) 2017-2018 Mattias Edlund
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
|
||||
namespace MeshDecimator.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// A resizable array.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The item type.</typeparam>
|
||||
internal sealed class ResizableArray<T>
|
||||
{
|
||||
#region Fields
|
||||
private T[] items = null;
|
||||
private int length = 0;
|
||||
|
||||
private static T[] emptyArr = new T[0];
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Gets the length of this array.
|
||||
/// </summary>
|
||||
public int Length
|
||||
{
|
||||
get { return length; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the internal data buffer for this array.
|
||||
/// </summary>
|
||||
public T[] Data
|
||||
{
|
||||
get { return items; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the element value at a specific index.
|
||||
/// </summary>
|
||||
/// <param name="index">The element index.</param>
|
||||
/// <returns>The element value.</returns>
|
||||
public T this[int index]
|
||||
{
|
||||
get { return items[index]; }
|
||||
set { items[index] = value; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Creates a new resizable array.
|
||||
/// </summary>
|
||||
/// <param name="capacity">The initial array capacity.</param>
|
||||
public ResizableArray(int capacity)
|
||||
: this(capacity, 0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new resizable array.
|
||||
/// </summary>
|
||||
/// <param name="capacity">The initial array capacity.</param>
|
||||
/// <param name="length">The initial length of the array.</param>
|
||||
public ResizableArray(int capacity, int length)
|
||||
{
|
||||
if (capacity < 0)
|
||||
throw new ArgumentOutOfRangeException("capacity");
|
||||
else if (length < 0 || length > capacity)
|
||||
throw new ArgumentOutOfRangeException("length");
|
||||
|
||||
if (capacity > 0)
|
||||
items = new T[capacity];
|
||||
else
|
||||
items = emptyArr;
|
||||
|
||||
this.length = length;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
private void IncreaseCapacity(int capacity)
|
||||
{
|
||||
T[] newItems = new T[capacity];
|
||||
Array.Copy(items, 0, newItems, 0, System.Math.Min(length, capacity));
|
||||
items = newItems;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
/// <summary>
|
||||
/// Clears this array.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
Array.Clear(items, 0, length);
|
||||
length = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resizes this array.
|
||||
/// </summary>
|
||||
/// <param name="length">The new length.</param>
|
||||
/// <param name="trimExess">If exess memory should be trimmed.</param>
|
||||
public void Resize(int length, bool trimExess = false)
|
||||
{
|
||||
if (length < 0)
|
||||
throw new ArgumentOutOfRangeException("capacity");
|
||||
|
||||
if (length > items.Length)
|
||||
{
|
||||
IncreaseCapacity(length);
|
||||
}
|
||||
else if (length < this.length)
|
||||
{
|
||||
//Array.Clear(items, capacity, length - capacity);
|
||||
}
|
||||
|
||||
this.length = length;
|
||||
|
||||
if (trimExess)
|
||||
{
|
||||
TrimExcess();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trims any excess memory for this array.
|
||||
/// </summary>
|
||||
public void TrimExcess()
|
||||
{
|
||||
if (items.Length == length) // Nothing to do
|
||||
return;
|
||||
|
||||
T[] newItems = new T[length];
|
||||
Array.Copy(items, 0, newItems, 0, length);
|
||||
items = newItems;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new item to the end of this array.
|
||||
/// </summary>
|
||||
/// <param name="item">The new item.</param>
|
||||
public void Add(T item)
|
||||
{
|
||||
if (length >= items.Length)
|
||||
{
|
||||
IncreaseCapacity(items.Length << 1);
|
||||
}
|
||||
|
||||
items[length++] = item;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
79
LightlessSync/ThirdParty/MeshDecimator/Collections/UVChannels.cs
vendored
Normal file
79
LightlessSync/ThirdParty/MeshDecimator/Collections/UVChannels.cs
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
|
||||
namespace MeshDecimator.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// A collection of UV channels.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVec">The UV vector type.</typeparam>
|
||||
internal sealed class UVChannels<TVec>
|
||||
{
|
||||
#region Fields
|
||||
private ResizableArray<TVec>[] channels = null;
|
||||
private TVec[][] channelsData = null;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Gets the channel collection data.
|
||||
/// </summary>
|
||||
public TVec[][] Data
|
||||
{
|
||||
get
|
||||
{
|
||||
for (int i = 0; i < Mesh.UVChannelCount; i++)
|
||||
{
|
||||
if (channels[i] != null)
|
||||
{
|
||||
channelsData[i] = channels[i].Data;
|
||||
}
|
||||
else
|
||||
{
|
||||
channelsData[i] = null;
|
||||
}
|
||||
}
|
||||
return channelsData;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a specific channel by index.
|
||||
/// </summary>
|
||||
/// <param name="index">The channel index.</param>
|
||||
public ResizableArray<TVec> this[int index]
|
||||
{
|
||||
get { return channels[index]; }
|
||||
set { channels[index] = value; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Creates a new collection of UV channels.
|
||||
/// </summary>
|
||||
public UVChannels()
|
||||
{
|
||||
channels = new ResizableArray<TVec>[Mesh.UVChannelCount];
|
||||
channelsData = new TVec[Mesh.UVChannelCount][];
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
/// <summary>
|
||||
/// Resizes all channels at once.
|
||||
/// </summary>
|
||||
/// <param name="capacity">The new capacity.</param>
|
||||
/// <param name="trimExess">If exess memory should be trimmed.</param>
|
||||
public void Resize(int capacity, bool trimExess = false)
|
||||
{
|
||||
for (int i = 0; i < Mesh.UVChannelCount; i++)
|
||||
{
|
||||
if (channels[i] != null)
|
||||
{
|
||||
channels[i].Resize(capacity, trimExess);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
21
LightlessSync/ThirdParty/MeshDecimator/LICENSE.md
vendored
Normal file
21
LightlessSync/ThirdParty/MeshDecimator/LICENSE.md
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017-2018 Mattias Edlund
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
286
LightlessSync/ThirdParty/MeshDecimator/Math/MathHelper.cs
vendored
Normal file
286
LightlessSync/ThirdParty/MeshDecimator/Math/MathHelper.cs
vendored
Normal file
@@ -0,0 +1,286 @@
|
||||
#region License
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright(c) 2017-2018 Mattias Edlund
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
|
||||
namespace MeshDecimator.Math
|
||||
{
|
||||
/// <summary>
|
||||
/// Math helpers.
|
||||
/// </summary>
|
||||
public static class MathHelper
|
||||
{
|
||||
#region Consts
|
||||
/// <summary>
|
||||
/// The Pi constant.
|
||||
/// </summary>
|
||||
public const float PI = 3.14159274f;
|
||||
|
||||
/// <summary>
|
||||
/// The Pi constant.
|
||||
/// </summary>
|
||||
public const double PId = 3.1415926535897932384626433832795;
|
||||
|
||||
/// <summary>
|
||||
/// Degrees to radian constant.
|
||||
/// </summary>
|
||||
public const float Deg2Rad = PI / 180f;
|
||||
|
||||
/// <summary>
|
||||
/// Degrees to radian constant.
|
||||
/// </summary>
|
||||
public const double Deg2Radd = PId / 180.0;
|
||||
|
||||
/// <summary>
|
||||
/// Radians to degrees constant.
|
||||
/// </summary>
|
||||
public const float Rad2Deg = 180f / PI;
|
||||
|
||||
/// <summary>
|
||||
/// Radians to degrees constant.
|
||||
/// </summary>
|
||||
public const double Rad2Degd = 180.0 / PId;
|
||||
#endregion
|
||||
|
||||
#region Min
|
||||
/// <summary>
|
||||
/// Returns the minimum of two values.
|
||||
/// </summary>
|
||||
/// <param name="val1">The first value.</param>
|
||||
/// <param name="val2">The second value.</param>
|
||||
/// <returns>The minimum value.</returns>
|
||||
public static int Min(int val1, int val2)
|
||||
{
|
||||
return (val1 < val2 ? val1 : val2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the minimum of three values.
|
||||
/// </summary>
|
||||
/// <param name="val1">The first value.</param>
|
||||
/// <param name="val2">The second value.</param>
|
||||
/// <param name="val3">The third value.</param>
|
||||
/// <returns>The minimum value.</returns>
|
||||
public static int Min(int val1, int val2, int val3)
|
||||
{
|
||||
return (val1 < val2 ? (val1 < val3 ? val1 : val3) : (val2 < val3 ? val2 : val3));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the minimum of two values.
|
||||
/// </summary>
|
||||
/// <param name="val1">The first value.</param>
|
||||
/// <param name="val2">The second value.</param>
|
||||
/// <returns>The minimum value.</returns>
|
||||
public static float Min(float val1, float val2)
|
||||
{
|
||||
return (val1 < val2 ? val1 : val2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the minimum of three values.
|
||||
/// </summary>
|
||||
/// <param name="val1">The first value.</param>
|
||||
/// <param name="val2">The second value.</param>
|
||||
/// <param name="val3">The third value.</param>
|
||||
/// <returns>The minimum value.</returns>
|
||||
public static float Min(float val1, float val2, float val3)
|
||||
{
|
||||
return (val1 < val2 ? (val1 < val3 ? val1 : val3) : (val2 < val3 ? val2 : val3));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the minimum of two values.
|
||||
/// </summary>
|
||||
/// <param name="val1">The first value.</param>
|
||||
/// <param name="val2">The second value.</param>
|
||||
/// <returns>The minimum value.</returns>
|
||||
public static double Min(double val1, double val2)
|
||||
{
|
||||
return (val1 < val2 ? val1 : val2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the minimum of three values.
|
||||
/// </summary>
|
||||
/// <param name="val1">The first value.</param>
|
||||
/// <param name="val2">The second value.</param>
|
||||
/// <param name="val3">The third value.</param>
|
||||
/// <returns>The minimum value.</returns>
|
||||
public static double Min(double val1, double val2, double val3)
|
||||
{
|
||||
return (val1 < val2 ? (val1 < val3 ? val1 : val3) : (val2 < val3 ? val2 : val3));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Max
|
||||
/// <summary>
|
||||
/// Returns the maximum of two values.
|
||||
/// </summary>
|
||||
/// <param name="val1">The first value.</param>
|
||||
/// <param name="val2">The second value.</param>
|
||||
/// <returns>The maximum value.</returns>
|
||||
public static int Max(int val1, int val2)
|
||||
{
|
||||
return (val1 > val2 ? val1 : val2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the maximum of three values.
|
||||
/// </summary>
|
||||
/// <param name="val1">The first value.</param>
|
||||
/// <param name="val2">The second value.</param>
|
||||
/// <param name="val3">The third value.</param>
|
||||
/// <returns>The maximum value.</returns>
|
||||
public static int Max(int val1, int val2, int val3)
|
||||
{
|
||||
return (val1 > val2 ? (val1 > val3 ? val1 : val3) : (val2 > val3 ? val2 : val3));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the maximum of two values.
|
||||
/// </summary>
|
||||
/// <param name="val1">The first value.</param>
|
||||
/// <param name="val2">The second value.</param>
|
||||
/// <returns>The maximum value.</returns>
|
||||
public static float Max(float val1, float val2)
|
||||
{
|
||||
return (val1 > val2 ? val1 : val2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the maximum of three values.
|
||||
/// </summary>
|
||||
/// <param name="val1">The first value.</param>
|
||||
/// <param name="val2">The second value.</param>
|
||||
/// <param name="val3">The third value.</param>
|
||||
/// <returns>The maximum value.</returns>
|
||||
public static float Max(float val1, float val2, float val3)
|
||||
{
|
||||
return (val1 > val2 ? (val1 > val3 ? val1 : val3) : (val2 > val3 ? val2 : val3));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the maximum of two values.
|
||||
/// </summary>
|
||||
/// <param name="val1">The first value.</param>
|
||||
/// <param name="val2">The second value.</param>
|
||||
/// <returns>The maximum value.</returns>
|
||||
public static double Max(double val1, double val2)
|
||||
{
|
||||
return (val1 > val2 ? val1 : val2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the maximum of three values.
|
||||
/// </summary>
|
||||
/// <param name="val1">The first value.</param>
|
||||
/// <param name="val2">The second value.</param>
|
||||
/// <param name="val3">The third value.</param>
|
||||
/// <returns>The maximum value.</returns>
|
||||
public static double Max(double val1, double val2, double val3)
|
||||
{
|
||||
return (val1 > val2 ? (val1 > val3 ? val1 : val3) : (val2 > val3 ? val2 : val3));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Clamping
|
||||
/// <summary>
|
||||
/// Clamps a value between a minimum and a maximum value.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to clamp.</param>
|
||||
/// <param name="min">The minimum value.</param>
|
||||
/// <param name="max">The maximum value.</param>
|
||||
/// <returns>The clamped value.</returns>
|
||||
public static float Clamp(float value, float min, float max)
|
||||
{
|
||||
return (value >= min ? (value <= max ? value : max) : min);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps a value between a minimum and a maximum value.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to clamp.</param>
|
||||
/// <param name="min">The minimum value.</param>
|
||||
/// <param name="max">The maximum value.</param>
|
||||
/// <returns>The clamped value.</returns>
|
||||
public static double Clamp(double value, double min, double max)
|
||||
{
|
||||
return (value >= min ? (value <= max ? value : max) : min);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps the value between 0 and 1.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to clamp.</param>
|
||||
/// <returns>The clamped value.</returns>
|
||||
public static float Clamp01(float value)
|
||||
{
|
||||
return (value > 0f ? (value < 1f ? value : 1f) : 0f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps the value between 0 and 1.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to clamp.</param>
|
||||
/// <returns>The clamped value.</returns>
|
||||
public static double Clamp01(double value)
|
||||
{
|
||||
return (value > 0.0 ? (value < 1.0 ? value : 1.0) : 0.0);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Triangle Area
|
||||
/// <summary>
|
||||
/// Calculates the area of a triangle.
|
||||
/// </summary>
|
||||
/// <param name="p0">The first point.</param>
|
||||
/// <param name="p1">The second point.</param>
|
||||
/// <param name="p2">The third point.</param>
|
||||
/// <returns>The triangle area.</returns>
|
||||
public static float TriangleArea(ref Vector3 p0, ref Vector3 p1, ref Vector3 p2)
|
||||
{
|
||||
var dx = p1 - p0;
|
||||
var dy = p2 - p0;
|
||||
return dx.Magnitude * ((float)System.Math.Sin(Vector3.Angle(ref dx, ref dy) * Deg2Rad) * dy.Magnitude) * 0.5f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the area of a triangle.
|
||||
/// </summary>
|
||||
/// <param name="p0">The first point.</param>
|
||||
/// <param name="p1">The second point.</param>
|
||||
/// <param name="p2">The third point.</param>
|
||||
/// <returns>The triangle area.</returns>
|
||||
public static double TriangleArea(ref Vector3d p0, ref Vector3d p1, ref Vector3d p2)
|
||||
{
|
||||
var dx = p1 - p0;
|
||||
var dy = p2 - p0;
|
||||
return dx.Magnitude * (System.Math.Sin(Vector3d.Angle(ref dx, ref dy) * Deg2Radd) * dy.Magnitude) * 0.5f;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
303
LightlessSync/ThirdParty/MeshDecimator/Math/SymmetricMatrix.cs
vendored
Normal file
303
LightlessSync/ThirdParty/MeshDecimator/Math/SymmetricMatrix.cs
vendored
Normal file
@@ -0,0 +1,303 @@
|
||||
#region License
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright(c) 2017-2018 Mattias Edlund
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
|
||||
namespace MeshDecimator.Math
|
||||
{
|
||||
/// <summary>
|
||||
/// A symmetric matrix.
|
||||
/// </summary>
|
||||
public struct SymmetricMatrix
|
||||
{
|
||||
#region Fields
|
||||
/// <summary>
|
||||
/// The m11 component.
|
||||
/// </summary>
|
||||
public double m0;
|
||||
/// <summary>
|
||||
/// The m12 component.
|
||||
/// </summary>
|
||||
public double m1;
|
||||
/// <summary>
|
||||
/// The m13 component.
|
||||
/// </summary>
|
||||
public double m2;
|
||||
/// <summary>
|
||||
/// The m14 component.
|
||||
/// </summary>
|
||||
public double m3;
|
||||
/// <summary>
|
||||
/// The m22 component.
|
||||
/// </summary>
|
||||
public double m4;
|
||||
/// <summary>
|
||||
/// The m23 component.
|
||||
/// </summary>
|
||||
public double m5;
|
||||
/// <summary>
|
||||
/// The m24 component.
|
||||
/// </summary>
|
||||
public double m6;
|
||||
/// <summary>
|
||||
/// The m33 component.
|
||||
/// </summary>
|
||||
public double m7;
|
||||
/// <summary>
|
||||
/// The m34 component.
|
||||
/// </summary>
|
||||
public double m8;
|
||||
/// <summary>
|
||||
/// The m44 component.
|
||||
/// </summary>
|
||||
public double m9;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Gets the component value with a specific index.
|
||||
/// </summary>
|
||||
/// <param name="index">The component index.</param>
|
||||
/// <returns>The value.</returns>
|
||||
public double this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
return m0;
|
||||
case 1:
|
||||
return m1;
|
||||
case 2:
|
||||
return m2;
|
||||
case 3:
|
||||
return m3;
|
||||
case 4:
|
||||
return m4;
|
||||
case 5:
|
||||
return m5;
|
||||
case 6:
|
||||
return m6;
|
||||
case 7:
|
||||
return m7;
|
||||
case 8:
|
||||
return m8;
|
||||
case 9:
|
||||
return m9;
|
||||
default:
|
||||
throw new IndexOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Creates a symmetric matrix with a value in each component.
|
||||
/// </summary>
|
||||
/// <param name="c">The component value.</param>
|
||||
public SymmetricMatrix(double c)
|
||||
{
|
||||
this.m0 = c;
|
||||
this.m1 = c;
|
||||
this.m2 = c;
|
||||
this.m3 = c;
|
||||
this.m4 = c;
|
||||
this.m5 = c;
|
||||
this.m6 = c;
|
||||
this.m7 = c;
|
||||
this.m8 = c;
|
||||
this.m9 = c;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a symmetric matrix.
|
||||
/// </summary>
|
||||
/// <param name="m0">The m11 component.</param>
|
||||
/// <param name="m1">The m12 component.</param>
|
||||
/// <param name="m2">The m13 component.</param>
|
||||
/// <param name="m3">The m14 component.</param>
|
||||
/// <param name="m4">The m22 component.</param>
|
||||
/// <param name="m5">The m23 component.</param>
|
||||
/// <param name="m6">The m24 component.</param>
|
||||
/// <param name="m7">The m33 component.</param>
|
||||
/// <param name="m8">The m34 component.</param>
|
||||
/// <param name="m9">The m44 component.</param>
|
||||
public SymmetricMatrix(double m0, double m1, double m2, double m3,
|
||||
double m4, double m5, double m6, double m7, double m8, double m9)
|
||||
{
|
||||
this.m0 = m0;
|
||||
this.m1 = m1;
|
||||
this.m2 = m2;
|
||||
this.m3 = m3;
|
||||
this.m4 = m4;
|
||||
this.m5 = m5;
|
||||
this.m6 = m6;
|
||||
this.m7 = m7;
|
||||
this.m8 = m8;
|
||||
this.m9 = m9;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a symmetric matrix from a plane.
|
||||
/// </summary>
|
||||
/// <param name="a">The plane x-component.</param>
|
||||
/// <param name="b">The plane y-component</param>
|
||||
/// <param name="c">The plane z-component</param>
|
||||
/// <param name="d">The plane w-component</param>
|
||||
public SymmetricMatrix(double a, double b, double c, double d)
|
||||
{
|
||||
this.m0 = a * a;
|
||||
this.m1 = a * b;
|
||||
this.m2 = a * c;
|
||||
this.m3 = a * d;
|
||||
|
||||
this.m4 = b * b;
|
||||
this.m5 = b * c;
|
||||
this.m6 = b * d;
|
||||
|
||||
this.m7 = c * c;
|
||||
this.m8 = c * d;
|
||||
|
||||
this.m9 = d * d;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
/// <summary>
|
||||
/// Adds two matrixes together.
|
||||
/// </summary>
|
||||
/// <param name="a">The left hand side.</param>
|
||||
/// <param name="b">The right hand side.</param>
|
||||
/// <returns>The resulting matrix.</returns>
|
||||
public static SymmetricMatrix operator +(SymmetricMatrix a, SymmetricMatrix b)
|
||||
{
|
||||
return new SymmetricMatrix(
|
||||
a.m0 + b.m0, a.m1 + b.m1, a.m2 + b.m2, a.m3 + b.m3,
|
||||
a.m4 + b.m4, a.m5 + b.m5, a.m6 + b.m6,
|
||||
a.m7 + b.m7, a.m8 + b.m8,
|
||||
a.m9 + b.m9
|
||||
);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Internal Methods
|
||||
/// <summary>
|
||||
/// Determinant(0, 1, 2, 1, 4, 5, 2, 5, 7)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal double Determinant1()
|
||||
{
|
||||
double det =
|
||||
m0 * m4 * m7 +
|
||||
m2 * m1 * m5 +
|
||||
m1 * m5 * m2 -
|
||||
m2 * m4 * m2 -
|
||||
m0 * m5 * m5 -
|
||||
m1 * m1 * m7;
|
||||
return det;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determinant(1, 2, 3, 4, 5, 6, 5, 7, 8)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal double Determinant2()
|
||||
{
|
||||
double det =
|
||||
m1 * m5 * m8 +
|
||||
m3 * m4 * m7 +
|
||||
m2 * m6 * m5 -
|
||||
m3 * m5 * m5 -
|
||||
m1 * m6 * m7 -
|
||||
m2 * m4 * m8;
|
||||
return det;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determinant(0, 2, 3, 1, 5, 6, 2, 7, 8)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal double Determinant3()
|
||||
{
|
||||
double det =
|
||||
m0 * m5 * m8 +
|
||||
m3 * m1 * m7 +
|
||||
m2 * m6 * m2 -
|
||||
m3 * m5 * m2 -
|
||||
m0 * m6 * m7 -
|
||||
m2 * m1 * m8;
|
||||
return det;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determinant(0, 1, 3, 1, 4, 6, 2, 5, 8)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal double Determinant4()
|
||||
{
|
||||
double det =
|
||||
m0 * m4 * m8 +
|
||||
m3 * m1 * m5 +
|
||||
m1 * m6 * m2 -
|
||||
m3 * m4 * m2 -
|
||||
m0 * m6 * m5 -
|
||||
m1 * m1 * m8;
|
||||
return det;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
/// <summary>
|
||||
/// Computes the determinant of this matrix.
|
||||
/// </summary>
|
||||
/// <param name="a11">The a11 index.</param>
|
||||
/// <param name="a12">The a12 index.</param>
|
||||
/// <param name="a13">The a13 index.</param>
|
||||
/// <param name="a21">The a21 index.</param>
|
||||
/// <param name="a22">The a22 index.</param>
|
||||
/// <param name="a23">The a23 index.</param>
|
||||
/// <param name="a31">The a31 index.</param>
|
||||
/// <param name="a32">The a32 index.</param>
|
||||
/// <param name="a33">The a33 index.</param>
|
||||
/// <returns>The determinant value.</returns>
|
||||
public double Determinant(int a11, int a12, int a13,
|
||||
int a21, int a22, int a23,
|
||||
int a31, int a32, int a33)
|
||||
{
|
||||
double det =
|
||||
this[a11] * this[a22] * this[a33] +
|
||||
this[a13] * this[a21] * this[a32] +
|
||||
this[a12] * this[a23] * this[a31] -
|
||||
this[a13] * this[a22] * this[a31] -
|
||||
this[a11] * this[a23] * this[a32] -
|
||||
this[a12] * this[a21] * this[a33];
|
||||
return det;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
425
LightlessSync/ThirdParty/MeshDecimator/Math/Vector2.cs
vendored
Normal file
425
LightlessSync/ThirdParty/MeshDecimator/Math/Vector2.cs
vendored
Normal file
@@ -0,0 +1,425 @@
|
||||
#region License
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright(c) 2017-2018 Mattias Edlund
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace MeshDecimator.Math
|
||||
{
|
||||
/// <summary>
|
||||
/// A single precision 2D vector.
|
||||
/// </summary>
|
||||
public struct Vector2 : IEquatable<Vector2>
|
||||
{
|
||||
#region Static Read-Only
|
||||
/// <summary>
|
||||
/// The zero vector.
|
||||
/// </summary>
|
||||
public static readonly Vector2 zero = new Vector2(0, 0);
|
||||
#endregion
|
||||
|
||||
#region Consts
|
||||
/// <summary>
|
||||
/// The vector epsilon.
|
||||
/// </summary>
|
||||
public const float Epsilon = 9.99999944E-11f;
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
/// <summary>
|
||||
/// The x component.
|
||||
/// </summary>
|
||||
public float x;
|
||||
/// <summary>
|
||||
/// The y component.
|
||||
/// </summary>
|
||||
public float y;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Gets the magnitude of this vector.
|
||||
/// </summary>
|
||||
public float Magnitude
|
||||
{
|
||||
get { return (float)System.Math.Sqrt(x * x + y * y); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the squared magnitude of this vector.
|
||||
/// </summary>
|
||||
public float MagnitudeSqr
|
||||
{
|
||||
get { return (x * x + y * y); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a normalized vector from this vector.
|
||||
/// </summary>
|
||||
public Vector2 Normalized
|
||||
{
|
||||
get
|
||||
{
|
||||
Vector2 result;
|
||||
Normalize(ref this, out result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a specific component by index in this vector.
|
||||
/// </summary>
|
||||
/// <param name="index">The component index.</param>
|
||||
public float this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
return x;
|
||||
case 1:
|
||||
return y;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector2 index!");
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
x = value;
|
||||
break;
|
||||
case 1:
|
||||
y = value;
|
||||
break;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector2 index!");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Creates a new vector with one value for all components.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
public Vector2(float value)
|
||||
{
|
||||
this.x = value;
|
||||
this.y = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new vector.
|
||||
/// </summary>
|
||||
/// <param name="x">The x value.</param>
|
||||
/// <param name="y">The y value.</param>
|
||||
public Vector2(float x, float y)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
/// <summary>
|
||||
/// Adds two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector2 operator +(Vector2 a, Vector2 b)
|
||||
{
|
||||
return new Vector2(a.x + b.x, a.y + b.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector2 operator -(Vector2 a, Vector2 b)
|
||||
{
|
||||
return new Vector2(a.x - b.x, a.y - b.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales the vector uniformly.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <param name="d">The scaling value.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector2 operator *(Vector2 a, float d)
|
||||
{
|
||||
return new Vector2(a.x * d, a.y * d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales the vector uniformly.
|
||||
/// </summary>
|
||||
/// <param name="d">The scaling value.</param>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector2 operator *(float d, Vector2 a)
|
||||
{
|
||||
return new Vector2(a.x * d, a.y * d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides the vector with a float.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <param name="d">The dividing float value.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector2 operator /(Vector2 a, float d)
|
||||
{
|
||||
return new Vector2(a.x / d, a.y / d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts the vector from a zero vector.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector2 operator -(Vector2 a)
|
||||
{
|
||||
return new Vector2(-a.x, -a.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if two vectors equals eachother.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public static bool operator ==(Vector2 lhs, Vector2 rhs)
|
||||
{
|
||||
return (lhs - rhs).MagnitudeSqr < Epsilon;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if two vectors don't equal eachother.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
/// <returns>If not equals.</returns>
|
||||
public static bool operator !=(Vector2 lhs, Vector2 rhs)
|
||||
{
|
||||
return (lhs - rhs).MagnitudeSqr >= Epsilon;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicitly converts from a double-precision vector into a single-precision vector.
|
||||
/// </summary>
|
||||
/// <param name="v">The double-precision vector.</param>
|
||||
public static explicit operator Vector2(Vector2d v)
|
||||
{
|
||||
return new Vector2((float)v.x, (float)v.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly converts from an integer vector into a single-precision vector.
|
||||
/// </summary>
|
||||
/// <param name="v">The integer vector.</param>
|
||||
public static implicit operator Vector2(Vector2i v)
|
||||
{
|
||||
return new Vector2(v.x, v.y);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
#region Instance
|
||||
/// <summary>
|
||||
/// Set x and y components of an existing vector.
|
||||
/// </summary>
|
||||
/// <param name="x">The x value.</param>
|
||||
/// <param name="y">The y value.</param>
|
||||
public void Set(float x, float y)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies with another vector component-wise.
|
||||
/// </summary>
|
||||
/// <param name="scale">The vector to multiply with.</param>
|
||||
public void Scale(ref Vector2 scale)
|
||||
{
|
||||
x *= scale.x;
|
||||
y *= scale.y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes this vector.
|
||||
/// </summary>
|
||||
public void Normalize()
|
||||
{
|
||||
float mag = this.Magnitude;
|
||||
if (mag > Epsilon)
|
||||
{
|
||||
x /= mag;
|
||||
y /= mag;
|
||||
}
|
||||
else
|
||||
{
|
||||
x = y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps this vector between a specific range.
|
||||
/// </summary>
|
||||
/// <param name="min">The minimum component value.</param>
|
||||
/// <param name="max">The maximum component value.</param>
|
||||
public void Clamp(float min, float max)
|
||||
{
|
||||
if (x < min) x = min;
|
||||
else if (x > max) x = max;
|
||||
|
||||
if (y < min) y = min;
|
||||
else if (y > max) y = max;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Object
|
||||
/// <summary>
|
||||
/// Returns a hash code for this vector.
|
||||
/// </summary>
|
||||
/// <returns>The hash code.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return x.GetHashCode() ^ y.GetHashCode() << 2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if this vector is equal to another one.
|
||||
/// </summary>
|
||||
/// <param name="other">The other vector to compare to.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
if (!(other is Vector2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Vector2 vector = (Vector2)other;
|
||||
return (x == vector.x && y == vector.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if this vector is equal to another one.
|
||||
/// </summary>
|
||||
/// <param name="other">The other vector to compare to.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public bool Equals(Vector2 other)
|
||||
{
|
||||
return (x == other.x && y == other.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a nicely formatted string for this vector.
|
||||
/// </summary>
|
||||
/// <returns>The string.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("({0}, {1})",
|
||||
x.ToString("F1", CultureInfo.InvariantCulture),
|
||||
y.ToString("F1", CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a nicely formatted string for this vector.
|
||||
/// </summary>
|
||||
/// <param name="format">The float format.</param>
|
||||
/// <returns>The string.</returns>
|
||||
public string ToString(string format)
|
||||
{
|
||||
return string.Format("({0}, {1})",
|
||||
x.ToString(format, CultureInfo.InvariantCulture),
|
||||
y.ToString(format, CultureInfo.InvariantCulture));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Static
|
||||
/// <summary>
|
||||
/// Dot Product of two vectors.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
public static float Dot(ref Vector2 lhs, ref Vector2 rhs)
|
||||
{
|
||||
return lhs.x * rhs.x + lhs.y * rhs.y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a linear interpolation between two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector to interpolate from.</param>
|
||||
/// <param name="b">The vector to interpolate to.</param>
|
||||
/// <param name="t">The time fraction.</param>
|
||||
/// <param name="result">The resulting vector.</param>
|
||||
public static void Lerp(ref Vector2 a, ref Vector2 b, float t, out Vector2 result)
|
||||
{
|
||||
result = new Vector2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies two vectors component-wise.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <param name="result">The resulting vector.</param>
|
||||
public static void Scale(ref Vector2 a, ref Vector2 b, out Vector2 result)
|
||||
{
|
||||
result = new Vector2(a.x * b.x, a.y * b.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes a vector.
|
||||
/// </summary>
|
||||
/// <param name="value">The vector to normalize.</param>
|
||||
/// <param name="result">The resulting normalized vector.</param>
|
||||
public static void Normalize(ref Vector2 value, out Vector2 result)
|
||||
{
|
||||
float mag = value.Magnitude;
|
||||
if (mag > Epsilon)
|
||||
{
|
||||
result = new Vector2(value.x / mag, value.y / mag);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = Vector2.zero;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
425
LightlessSync/ThirdParty/MeshDecimator/Math/Vector2d.cs
vendored
Normal file
425
LightlessSync/ThirdParty/MeshDecimator/Math/Vector2d.cs
vendored
Normal file
@@ -0,0 +1,425 @@
|
||||
#region License
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright(c) 2017-2018 Mattias Edlund
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace MeshDecimator.Math
|
||||
{
|
||||
/// <summary>
|
||||
/// A double precision 2D vector.
|
||||
/// </summary>
|
||||
public struct Vector2d : IEquatable<Vector2d>
|
||||
{
|
||||
#region Static Read-Only
|
||||
/// <summary>
|
||||
/// The zero vector.
|
||||
/// </summary>
|
||||
public static readonly Vector2d zero = new Vector2d(0, 0);
|
||||
#endregion
|
||||
|
||||
#region Consts
|
||||
/// <summary>
|
||||
/// The vector epsilon.
|
||||
/// </summary>
|
||||
public const double Epsilon = double.Epsilon;
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
/// <summary>
|
||||
/// The x component.
|
||||
/// </summary>
|
||||
public double x;
|
||||
/// <summary>
|
||||
/// The y component.
|
||||
/// </summary>
|
||||
public double y;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Gets the magnitude of this vector.
|
||||
/// </summary>
|
||||
public double Magnitude
|
||||
{
|
||||
get { return System.Math.Sqrt(x * x + y * y); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the squared magnitude of this vector.
|
||||
/// </summary>
|
||||
public double MagnitudeSqr
|
||||
{
|
||||
get { return (x * x + y * y); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a normalized vector from this vector.
|
||||
/// </summary>
|
||||
public Vector2d Normalized
|
||||
{
|
||||
get
|
||||
{
|
||||
Vector2d result;
|
||||
Normalize(ref this, out result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a specific component by index in this vector.
|
||||
/// </summary>
|
||||
/// <param name="index">The component index.</param>
|
||||
public double this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
return x;
|
||||
case 1:
|
||||
return y;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector2d index!");
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
x = value;
|
||||
break;
|
||||
case 1:
|
||||
y = value;
|
||||
break;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector2d index!");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Creates a new vector with one value for all components.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
public Vector2d(double value)
|
||||
{
|
||||
this.x = value;
|
||||
this.y = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new vector.
|
||||
/// </summary>
|
||||
/// <param name="x">The x value.</param>
|
||||
/// <param name="y">The y value.</param>
|
||||
public Vector2d(double x, double y)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
/// <summary>
|
||||
/// Adds two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector2d operator +(Vector2d a, Vector2d b)
|
||||
{
|
||||
return new Vector2d(a.x + b.x, a.y + b.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector2d operator -(Vector2d a, Vector2d b)
|
||||
{
|
||||
return new Vector2d(a.x - b.x, a.y - b.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales the vector uniformly.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <param name="d">The scaling value.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector2d operator *(Vector2d a, double d)
|
||||
{
|
||||
return new Vector2d(a.x * d, a.y * d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales the vector uniformly.
|
||||
/// </summary>
|
||||
/// <param name="d">The scaling value.</param>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector2d operator *(double d, Vector2d a)
|
||||
{
|
||||
return new Vector2d(a.x * d, a.y * d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides the vector with a float.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <param name="d">The dividing float value.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector2d operator /(Vector2d a, double d)
|
||||
{
|
||||
return new Vector2d(a.x / d, a.y / d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts the vector from a zero vector.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector2d operator -(Vector2d a)
|
||||
{
|
||||
return new Vector2d(-a.x, -a.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if two vectors equals eachother.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public static bool operator ==(Vector2d lhs, Vector2d rhs)
|
||||
{
|
||||
return (lhs - rhs).MagnitudeSqr < Epsilon;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if two vectors don't equal eachother.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
/// <returns>If not equals.</returns>
|
||||
public static bool operator !=(Vector2d lhs, Vector2d rhs)
|
||||
{
|
||||
return (lhs - rhs).MagnitudeSqr >= Epsilon;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly converts from a single-precision vector into a double-precision vector.
|
||||
/// </summary>
|
||||
/// <param name="v">The single-precision vector.</param>
|
||||
public static implicit operator Vector2d(Vector2 v)
|
||||
{
|
||||
return new Vector2d(v.x, v.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly converts from an integer vector into a double-precision vector.
|
||||
/// </summary>
|
||||
/// <param name="v">The integer vector.</param>
|
||||
public static implicit operator Vector2d(Vector2i v)
|
||||
{
|
||||
return new Vector2d(v.x, v.y);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
#region Instance
|
||||
/// <summary>
|
||||
/// Set x and y components of an existing vector.
|
||||
/// </summary>
|
||||
/// <param name="x">The x value.</param>
|
||||
/// <param name="y">The y value.</param>
|
||||
public void Set(double x, double y)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies with another vector component-wise.
|
||||
/// </summary>
|
||||
/// <param name="scale">The vector to multiply with.</param>
|
||||
public void Scale(ref Vector2d scale)
|
||||
{
|
||||
x *= scale.x;
|
||||
y *= scale.y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes this vector.
|
||||
/// </summary>
|
||||
public void Normalize()
|
||||
{
|
||||
double mag = this.Magnitude;
|
||||
if (mag > Epsilon)
|
||||
{
|
||||
x /= mag;
|
||||
y /= mag;
|
||||
}
|
||||
else
|
||||
{
|
||||
x = y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps this vector between a specific range.
|
||||
/// </summary>
|
||||
/// <param name="min">The minimum component value.</param>
|
||||
/// <param name="max">The maximum component value.</param>
|
||||
public void Clamp(double min, double max)
|
||||
{
|
||||
if (x < min) x = min;
|
||||
else if (x > max) x = max;
|
||||
|
||||
if (y < min) y = min;
|
||||
else if (y > max) y = max;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Object
|
||||
/// <summary>
|
||||
/// Returns a hash code for this vector.
|
||||
/// </summary>
|
||||
/// <returns>The hash code.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return x.GetHashCode() ^ y.GetHashCode() << 2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if this vector is equal to another one.
|
||||
/// </summary>
|
||||
/// <param name="other">The other vector to compare to.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
if (!(other is Vector2d))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Vector2d vector = (Vector2d)other;
|
||||
return (x == vector.x && y == vector.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if this vector is equal to another one.
|
||||
/// </summary>
|
||||
/// <param name="other">The other vector to compare to.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public bool Equals(Vector2d other)
|
||||
{
|
||||
return (x == other.x && y == other.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a nicely formatted string for this vector.
|
||||
/// </summary>
|
||||
/// <returns>The string.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("({0}, {1})",
|
||||
x.ToString("F1", CultureInfo.InvariantCulture),
|
||||
y.ToString("F1", CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a nicely formatted string for this vector.
|
||||
/// </summary>
|
||||
/// <param name="format">The float format.</param>
|
||||
/// <returns>The string.</returns>
|
||||
public string ToString(string format)
|
||||
{
|
||||
return string.Format("({0}, {1})",
|
||||
x.ToString(format, CultureInfo.InvariantCulture),
|
||||
y.ToString(format, CultureInfo.InvariantCulture));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Static
|
||||
/// <summary>
|
||||
/// Dot Product of two vectors.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
public static double Dot(ref Vector2d lhs, ref Vector2d rhs)
|
||||
{
|
||||
return lhs.x * rhs.x + lhs.y * rhs.y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a linear interpolation between two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector to interpolate from.</param>
|
||||
/// <param name="b">The vector to interpolate to.</param>
|
||||
/// <param name="t">The time fraction.</param>
|
||||
/// <param name="result">The resulting vector.</param>
|
||||
public static void Lerp(ref Vector2d a, ref Vector2d b, double t, out Vector2d result)
|
||||
{
|
||||
result = new Vector2d(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies two vectors component-wise.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <param name="result">The resulting vector.</param>
|
||||
public static void Scale(ref Vector2d a, ref Vector2d b, out Vector2d result)
|
||||
{
|
||||
result = new Vector2d(a.x * b.x, a.y * b.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes a vector.
|
||||
/// </summary>
|
||||
/// <param name="value">The vector to normalize.</param>
|
||||
/// <param name="result">The resulting normalized vector.</param>
|
||||
public static void Normalize(ref Vector2d value, out Vector2d result)
|
||||
{
|
||||
double mag = value.Magnitude;
|
||||
if (mag > Epsilon)
|
||||
{
|
||||
result = new Vector2d(value.x / mag, value.y / mag);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = Vector2d.zero;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
348
LightlessSync/ThirdParty/MeshDecimator/Math/Vector2i.cs
vendored
Normal file
348
LightlessSync/ThirdParty/MeshDecimator/Math/Vector2i.cs
vendored
Normal file
@@ -0,0 +1,348 @@
|
||||
#region License
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright(c) 2017-2018 Mattias Edlund
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace MeshDecimator.Math
|
||||
{
|
||||
/// <summary>
|
||||
/// A 2D integer vector.
|
||||
/// </summary>
|
||||
public struct Vector2i : IEquatable<Vector2i>
|
||||
{
|
||||
#region Static Read-Only
|
||||
/// <summary>
|
||||
/// The zero vector.
|
||||
/// </summary>
|
||||
public static readonly Vector2i zero = new Vector2i(0, 0);
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
/// <summary>
|
||||
/// The x component.
|
||||
/// </summary>
|
||||
public int x;
|
||||
/// <summary>
|
||||
/// The y component.
|
||||
/// </summary>
|
||||
public int y;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Gets the magnitude of this vector.
|
||||
/// </summary>
|
||||
public int Magnitude
|
||||
{
|
||||
get { return (int)System.Math.Sqrt(x * x + y * y); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the squared magnitude of this vector.
|
||||
/// </summary>
|
||||
public int MagnitudeSqr
|
||||
{
|
||||
get { return (x * x + y * y); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a specific component by index in this vector.
|
||||
/// </summary>
|
||||
/// <param name="index">The component index.</param>
|
||||
public int this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
return x;
|
||||
case 1:
|
||||
return y;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector2i index!");
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
x = value;
|
||||
break;
|
||||
case 1:
|
||||
y = value;
|
||||
break;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector2i index!");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Creates a new vector with one value for all components.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
public Vector2i(int value)
|
||||
{
|
||||
this.x = value;
|
||||
this.y = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new vector.
|
||||
/// </summary>
|
||||
/// <param name="x">The x value.</param>
|
||||
/// <param name="y">The y value.</param>
|
||||
public Vector2i(int x, int y)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
/// <summary>
|
||||
/// Adds two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector2i operator +(Vector2i a, Vector2i b)
|
||||
{
|
||||
return new Vector2i(a.x + b.x, a.y + b.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector2i operator -(Vector2i a, Vector2i b)
|
||||
{
|
||||
return new Vector2i(a.x - b.x, a.y - b.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales the vector uniformly.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <param name="d">The scaling value.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector2i operator *(Vector2i a, int d)
|
||||
{
|
||||
return new Vector2i(a.x * d, a.y * d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales the vector uniformly.
|
||||
/// </summary>
|
||||
/// <param name="d">The scaling value.</param>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector2i operator *(int d, Vector2i a)
|
||||
{
|
||||
return new Vector2i(a.x * d, a.y * d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides the vector with a float.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <param name="d">The dividing float value.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector2i operator /(Vector2i a, int d)
|
||||
{
|
||||
return new Vector2i(a.x / d, a.y / d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts the vector from a zero vector.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector2i operator -(Vector2i a)
|
||||
{
|
||||
return new Vector2i(-a.x, -a.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if two vectors equals eachother.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public static bool operator ==(Vector2i lhs, Vector2i rhs)
|
||||
{
|
||||
return (lhs.x == rhs.x && lhs.y == rhs.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if two vectors don't equal eachother.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
/// <returns>If not equals.</returns>
|
||||
public static bool operator !=(Vector2i lhs, Vector2i rhs)
|
||||
{
|
||||
return (lhs.x != rhs.x || lhs.y != rhs.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicitly converts from a single-precision vector into an integer vector.
|
||||
/// </summary>
|
||||
/// <param name="v">The single-precision vector.</param>
|
||||
public static explicit operator Vector2i(Vector2 v)
|
||||
{
|
||||
return new Vector2i((int)v.x, (int)v.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicitly converts from a double-precision vector into an integer vector.
|
||||
/// </summary>
|
||||
/// <param name="v">The double-precision vector.</param>
|
||||
public static explicit operator Vector2i(Vector2d v)
|
||||
{
|
||||
return new Vector2i((int)v.x, (int)v.y);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
#region Instance
|
||||
/// <summary>
|
||||
/// Set x and y components of an existing vector.
|
||||
/// </summary>
|
||||
/// <param name="x">The x value.</param>
|
||||
/// <param name="y">The y value.</param>
|
||||
public void Set(int x, int y)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies with another vector component-wise.
|
||||
/// </summary>
|
||||
/// <param name="scale">The vector to multiply with.</param>
|
||||
public void Scale(ref Vector2i scale)
|
||||
{
|
||||
x *= scale.x;
|
||||
y *= scale.y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps this vector between a specific range.
|
||||
/// </summary>
|
||||
/// <param name="min">The minimum component value.</param>
|
||||
/// <param name="max">The maximum component value.</param>
|
||||
public void Clamp(int min, int max)
|
||||
{
|
||||
if (x < min) x = min;
|
||||
else if (x > max) x = max;
|
||||
|
||||
if (y < min) y = min;
|
||||
else if (y > max) y = max;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Object
|
||||
/// <summary>
|
||||
/// Returns a hash code for this vector.
|
||||
/// </summary>
|
||||
/// <returns>The hash code.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return x.GetHashCode() ^ y.GetHashCode() << 2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if this vector is equal to another one.
|
||||
/// </summary>
|
||||
/// <param name="other">The other vector to compare to.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
if (!(other is Vector2i))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Vector2i vector = (Vector2i)other;
|
||||
return (x == vector.x && y == vector.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if this vector is equal to another one.
|
||||
/// </summary>
|
||||
/// <param name="other">The other vector to compare to.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public bool Equals(Vector2i other)
|
||||
{
|
||||
return (x == other.x && y == other.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a nicely formatted string for this vector.
|
||||
/// </summary>
|
||||
/// <returns>The string.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("({0}, {1})",
|
||||
x.ToString(CultureInfo.InvariantCulture),
|
||||
y.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a nicely formatted string for this vector.
|
||||
/// </summary>
|
||||
/// <param name="format">The integer format.</param>
|
||||
/// <returns>The string.</returns>
|
||||
public string ToString(string format)
|
||||
{
|
||||
return string.Format("({0}, {1})",
|
||||
x.ToString(format, CultureInfo.InvariantCulture),
|
||||
y.ToString(format, CultureInfo.InvariantCulture));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Static
|
||||
/// <summary>
|
||||
/// Multiplies two vectors component-wise.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <param name="result">The resulting vector.</param>
|
||||
public static void Scale(ref Vector2i a, ref Vector2i b, out Vector2i result)
|
||||
{
|
||||
result = new Vector2i(a.x * b.x, a.y * b.y);
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
494
LightlessSync/ThirdParty/MeshDecimator/Math/Vector3.cs
vendored
Normal file
494
LightlessSync/ThirdParty/MeshDecimator/Math/Vector3.cs
vendored
Normal file
@@ -0,0 +1,494 @@
|
||||
#region License
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright(c) 2017-2018 Mattias Edlund
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace MeshDecimator.Math
|
||||
{
|
||||
/// <summary>
|
||||
/// A single precision 3D vector.
|
||||
/// </summary>
|
||||
public struct Vector3 : IEquatable<Vector3>
|
||||
{
|
||||
#region Static Read-Only
|
||||
/// <summary>
|
||||
/// The zero vector.
|
||||
/// </summary>
|
||||
public static readonly Vector3 zero = new Vector3(0, 0, 0);
|
||||
#endregion
|
||||
|
||||
#region Consts
|
||||
/// <summary>
|
||||
/// The vector epsilon.
|
||||
/// </summary>
|
||||
public const float Epsilon = 9.99999944E-11f;
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
/// <summary>
|
||||
/// The x component.
|
||||
/// </summary>
|
||||
public float x;
|
||||
/// <summary>
|
||||
/// The y component.
|
||||
/// </summary>
|
||||
public float y;
|
||||
/// <summary>
|
||||
/// The z component.
|
||||
/// </summary>
|
||||
public float z;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Gets the magnitude of this vector.
|
||||
/// </summary>
|
||||
public float Magnitude
|
||||
{
|
||||
get { return (float)System.Math.Sqrt(x * x + y * y + z * z); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the squared magnitude of this vector.
|
||||
/// </summary>
|
||||
public float MagnitudeSqr
|
||||
{
|
||||
get { return (x * x + y * y + z * z); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a normalized vector from this vector.
|
||||
/// </summary>
|
||||
public Vector3 Normalized
|
||||
{
|
||||
get
|
||||
{
|
||||
Vector3 result;
|
||||
Normalize(ref this, out result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a specific component by index in this vector.
|
||||
/// </summary>
|
||||
/// <param name="index">The component index.</param>
|
||||
public float this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
return x;
|
||||
case 1:
|
||||
return y;
|
||||
case 2:
|
||||
return z;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector3 index!");
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
x = value;
|
||||
break;
|
||||
case 1:
|
||||
y = value;
|
||||
break;
|
||||
case 2:
|
||||
z = value;
|
||||
break;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector3 index!");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Creates a new vector with one value for all components.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
public Vector3(float value)
|
||||
{
|
||||
this.x = value;
|
||||
this.y = value;
|
||||
this.z = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new vector.
|
||||
/// </summary>
|
||||
/// <param name="x">The x value.</param>
|
||||
/// <param name="y">The y value.</param>
|
||||
/// <param name="z">The z value.</param>
|
||||
public Vector3(float x, float y, float z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new vector from a double precision vector.
|
||||
/// </summary>
|
||||
/// <param name="vector">The double precision vector.</param>
|
||||
public Vector3(Vector3d vector)
|
||||
{
|
||||
this.x = (float)vector.x;
|
||||
this.y = (float)vector.y;
|
||||
this.z = (float)vector.z;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
/// <summary>
|
||||
/// Adds two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector3 operator +(Vector3 a, Vector3 b)
|
||||
{
|
||||
return new Vector3(a.x + b.x, a.y + b.y, a.z + b.z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector3 operator -(Vector3 a, Vector3 b)
|
||||
{
|
||||
return new Vector3(a.x - b.x, a.y - b.y, a.z - b.z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales the vector uniformly.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <param name="d">The scaling value.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector3 operator *(Vector3 a, float d)
|
||||
{
|
||||
return new Vector3(a.x * d, a.y * d, a.z * d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales the vector uniformly.
|
||||
/// </summary>
|
||||
/// <param name="d">The scaling value.</param>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector3 operator *(float d, Vector3 a)
|
||||
{
|
||||
return new Vector3(a.x * d, a.y * d, a.z * d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides the vector with a float.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <param name="d">The dividing float value.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector3 operator /(Vector3 a, float d)
|
||||
{
|
||||
return new Vector3(a.x / d, a.y / d, a.z / d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts the vector from a zero vector.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector3 operator -(Vector3 a)
|
||||
{
|
||||
return new Vector3(-a.x, -a.y, -a.z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if two vectors equals eachother.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public static bool operator ==(Vector3 lhs, Vector3 rhs)
|
||||
{
|
||||
return (lhs - rhs).MagnitudeSqr < Epsilon;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if two vectors don't equal eachother.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
/// <returns>If not equals.</returns>
|
||||
public static bool operator !=(Vector3 lhs, Vector3 rhs)
|
||||
{
|
||||
return (lhs - rhs).MagnitudeSqr >= Epsilon;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicitly converts from a double-precision vector into a single-precision vector.
|
||||
/// </summary>
|
||||
/// <param name="v">The double-precision vector.</param>
|
||||
public static explicit operator Vector3(Vector3d v)
|
||||
{
|
||||
return new Vector3((float)v.x, (float)v.y, (float)v.z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly converts from an integer vector into a single-precision vector.
|
||||
/// </summary>
|
||||
/// <param name="v">The integer vector.</param>
|
||||
public static implicit operator Vector3(Vector3i v)
|
||||
{
|
||||
return new Vector3(v.x, v.y, v.z);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
#region Instance
|
||||
/// <summary>
|
||||
/// Set x, y and z components of an existing vector.
|
||||
/// </summary>
|
||||
/// <param name="x">The x value.</param>
|
||||
/// <param name="y">The y value.</param>
|
||||
/// <param name="z">The z value.</param>
|
||||
public void Set(float x, float y, float z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies with another vector component-wise.
|
||||
/// </summary>
|
||||
/// <param name="scale">The vector to multiply with.</param>
|
||||
public void Scale(ref Vector3 scale)
|
||||
{
|
||||
x *= scale.x;
|
||||
y *= scale.y;
|
||||
z *= scale.z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes this vector.
|
||||
/// </summary>
|
||||
public void Normalize()
|
||||
{
|
||||
float mag = this.Magnitude;
|
||||
if (mag > Epsilon)
|
||||
{
|
||||
x /= mag;
|
||||
y /= mag;
|
||||
z /= mag;
|
||||
}
|
||||
else
|
||||
{
|
||||
x = y = z = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps this vector between a specific range.
|
||||
/// </summary>
|
||||
/// <param name="min">The minimum component value.</param>
|
||||
/// <param name="max">The maximum component value.</param>
|
||||
public void Clamp(float min, float max)
|
||||
{
|
||||
if (x < min) x = min;
|
||||
else if (x > max) x = max;
|
||||
|
||||
if (y < min) y = min;
|
||||
else if (y > max) y = max;
|
||||
|
||||
if (z < min) z = min;
|
||||
else if (z > max) z = max;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Object
|
||||
/// <summary>
|
||||
/// Returns a hash code for this vector.
|
||||
/// </summary>
|
||||
/// <returns>The hash code.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return x.GetHashCode() ^ y.GetHashCode() << 2 ^ z.GetHashCode() >> 2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if this vector is equal to another one.
|
||||
/// </summary>
|
||||
/// <param name="other">The other vector to compare to.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
if (!(other is Vector3))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Vector3 vector = (Vector3)other;
|
||||
return (x == vector.x && y == vector.y && z == vector.z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if this vector is equal to another one.
|
||||
/// </summary>
|
||||
/// <param name="other">The other vector to compare to.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public bool Equals(Vector3 other)
|
||||
{
|
||||
return (x == other.x && y == other.y && z == other.z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a nicely formatted string for this vector.
|
||||
/// </summary>
|
||||
/// <returns>The string.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("({0}, {1}, {2})",
|
||||
x.ToString("F1", CultureInfo.InvariantCulture),
|
||||
y.ToString("F1", CultureInfo.InvariantCulture),
|
||||
z.ToString("F1", CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a nicely formatted string for this vector.
|
||||
/// </summary>
|
||||
/// <param name="format">The float format.</param>
|
||||
/// <returns>The string.</returns>
|
||||
public string ToString(string format)
|
||||
{
|
||||
return string.Format("({0}, {1}, {2})",
|
||||
x.ToString(format, CultureInfo.InvariantCulture),
|
||||
y.ToString(format, CultureInfo.InvariantCulture),
|
||||
z.ToString(format, CultureInfo.InvariantCulture));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Static
|
||||
/// <summary>
|
||||
/// Dot Product of two vectors.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
public static float Dot(ref Vector3 lhs, ref Vector3 rhs)
|
||||
{
|
||||
return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cross Product of two vectors.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
/// <param name="result">The resulting vector.</param>
|
||||
public static void Cross(ref Vector3 lhs, ref Vector3 rhs, out Vector3 result)
|
||||
{
|
||||
result = new Vector3(lhs.y * rhs.z - lhs.z * rhs.y, lhs.z * rhs.x - lhs.x * rhs.z, lhs.x * rhs.y - lhs.y * rhs.x);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the angle between two vectors.
|
||||
/// </summary>
|
||||
/// <param name="from">The from vector.</param>
|
||||
/// <param name="to">The to vector.</param>
|
||||
/// <returns>The angle.</returns>
|
||||
public static float Angle(ref Vector3 from, ref Vector3 to)
|
||||
{
|
||||
Vector3 fromNormalized = from.Normalized;
|
||||
Vector3 toNormalized = to.Normalized;
|
||||
return (float)System.Math.Acos(MathHelper.Clamp(Vector3.Dot(ref fromNormalized, ref toNormalized), -1f, 1f)) * MathHelper.Rad2Deg;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a linear interpolation between two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector to interpolate from.</param>
|
||||
/// <param name="b">The vector to interpolate to.</param>
|
||||
/// <param name="t">The time fraction.</param>
|
||||
/// <param name="result">The resulting vector.</param>
|
||||
public static void Lerp(ref Vector3 a, ref Vector3 b, float t, out Vector3 result)
|
||||
{
|
||||
result = new Vector3(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies two vectors component-wise.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <param name="result">The resulting vector.</param>
|
||||
public static void Scale(ref Vector3 a, ref Vector3 b, out Vector3 result)
|
||||
{
|
||||
result = new Vector3(a.x * b.x, a.y * b.y, a.z * b.z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes a vector.
|
||||
/// </summary>
|
||||
/// <param name="value">The vector to normalize.</param>
|
||||
/// <param name="result">The resulting normalized vector.</param>
|
||||
public static void Normalize(ref Vector3 value, out Vector3 result)
|
||||
{
|
||||
float mag = value.Magnitude;
|
||||
if (mag > Epsilon)
|
||||
{
|
||||
result = new Vector3(value.x / mag, value.y / mag, value.z / mag);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = Vector3.zero;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes both vectors and makes them orthogonal to each other.
|
||||
/// </summary>
|
||||
/// <param name="normal">The normal vector.</param>
|
||||
/// <param name="tangent">The tangent.</param>
|
||||
public static void OrthoNormalize(ref Vector3 normal, ref Vector3 tangent)
|
||||
{
|
||||
normal.Normalize();
|
||||
Vector3 proj = normal * Vector3.Dot(ref tangent, ref normal);
|
||||
tangent -= proj;
|
||||
tangent.Normalize();
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
481
LightlessSync/ThirdParty/MeshDecimator/Math/Vector3d.cs
vendored
Normal file
481
LightlessSync/ThirdParty/MeshDecimator/Math/Vector3d.cs
vendored
Normal file
@@ -0,0 +1,481 @@
|
||||
#region License
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright(c) 2017-2018 Mattias Edlund
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace MeshDecimator.Math
|
||||
{
|
||||
/// <summary>
|
||||
/// A double precision 3D vector.
|
||||
/// </summary>
|
||||
public struct Vector3d : IEquatable<Vector3d>
|
||||
{
|
||||
#region Static Read-Only
|
||||
/// <summary>
|
||||
/// The zero vector.
|
||||
/// </summary>
|
||||
public static readonly Vector3d zero = new Vector3d(0, 0, 0);
|
||||
#endregion
|
||||
|
||||
#region Consts
|
||||
/// <summary>
|
||||
/// The vector epsilon.
|
||||
/// </summary>
|
||||
public const double Epsilon = double.Epsilon;
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
/// <summary>
|
||||
/// The x component.
|
||||
/// </summary>
|
||||
public double x;
|
||||
/// <summary>
|
||||
/// The y component.
|
||||
/// </summary>
|
||||
public double y;
|
||||
/// <summary>
|
||||
/// The z component.
|
||||
/// </summary>
|
||||
public double z;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Gets the magnitude of this vector.
|
||||
/// </summary>
|
||||
public double Magnitude
|
||||
{
|
||||
get { return System.Math.Sqrt(x * x + y * y + z * z); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the squared magnitude of this vector.
|
||||
/// </summary>
|
||||
public double MagnitudeSqr
|
||||
{
|
||||
get { return (x * x + y * y + z * z); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a normalized vector from this vector.
|
||||
/// </summary>
|
||||
public Vector3d Normalized
|
||||
{
|
||||
get
|
||||
{
|
||||
Vector3d result;
|
||||
Normalize(ref this, out result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a specific component by index in this vector.
|
||||
/// </summary>
|
||||
/// <param name="index">The component index.</param>
|
||||
public double this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
return x;
|
||||
case 1:
|
||||
return y;
|
||||
case 2:
|
||||
return z;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector3d index!");
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
x = value;
|
||||
break;
|
||||
case 1:
|
||||
y = value;
|
||||
break;
|
||||
case 2:
|
||||
z = value;
|
||||
break;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector3d index!");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Creates a new vector with one value for all components.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
public Vector3d(double value)
|
||||
{
|
||||
this.x = value;
|
||||
this.y = value;
|
||||
this.z = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new vector.
|
||||
/// </summary>
|
||||
/// <param name="x">The x value.</param>
|
||||
/// <param name="y">The y value.</param>
|
||||
/// <param name="z">The z value.</param>
|
||||
public Vector3d(double x, double y, double z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new vector from a single precision vector.
|
||||
/// </summary>
|
||||
/// <param name="vector">The single precision vector.</param>
|
||||
public Vector3d(Vector3 vector)
|
||||
{
|
||||
this.x = vector.x;
|
||||
this.y = vector.y;
|
||||
this.z = vector.z;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
/// <summary>
|
||||
/// Adds two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector3d operator +(Vector3d a, Vector3d b)
|
||||
{
|
||||
return new Vector3d(a.x + b.x, a.y + b.y, a.z + b.z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector3d operator -(Vector3d a, Vector3d b)
|
||||
{
|
||||
return new Vector3d(a.x - b.x, a.y - b.y, a.z - b.z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales the vector uniformly.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <param name="d">The scaling value.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector3d operator *(Vector3d a, double d)
|
||||
{
|
||||
return new Vector3d(a.x * d, a.y * d, a.z * d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales the vector uniformly.
|
||||
/// </summary>
|
||||
/// <param name="d">The scaling value.</param>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector3d operator *(double d, Vector3d a)
|
||||
{
|
||||
return new Vector3d(a.x * d, a.y * d, a.z * d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides the vector with a float.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <param name="d">The dividing float value.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector3d operator /(Vector3d a, double d)
|
||||
{
|
||||
return new Vector3d(a.x / d, a.y / d, a.z / d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts the vector from a zero vector.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector3d operator -(Vector3d a)
|
||||
{
|
||||
return new Vector3d(-a.x, -a.y, -a.z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if two vectors equals eachother.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public static bool operator ==(Vector3d lhs, Vector3d rhs)
|
||||
{
|
||||
return (lhs - rhs).MagnitudeSqr < Epsilon;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if two vectors don't equal eachother.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
/// <returns>If not equals.</returns>
|
||||
public static bool operator !=(Vector3d lhs, Vector3d rhs)
|
||||
{
|
||||
return (lhs - rhs).MagnitudeSqr >= Epsilon;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly converts from a single-precision vector into a double-precision vector.
|
||||
/// </summary>
|
||||
/// <param name="v">The single-precision vector.</param>
|
||||
public static implicit operator Vector3d(Vector3 v)
|
||||
{
|
||||
return new Vector3d(v.x, v.y, v.z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly converts from an integer vector into a double-precision vector.
|
||||
/// </summary>
|
||||
/// <param name="v">The integer vector.</param>
|
||||
public static implicit operator Vector3d(Vector3i v)
|
||||
{
|
||||
return new Vector3d(v.x, v.y, v.z);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
#region Instance
|
||||
/// <summary>
|
||||
/// Set x, y and z components of an existing vector.
|
||||
/// </summary>
|
||||
/// <param name="x">The x value.</param>
|
||||
/// <param name="y">The y value.</param>
|
||||
/// <param name="z">The z value.</param>
|
||||
public void Set(double x, double y, double z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies with another vector component-wise.
|
||||
/// </summary>
|
||||
/// <param name="scale">The vector to multiply with.</param>
|
||||
public void Scale(ref Vector3d scale)
|
||||
{
|
||||
x *= scale.x;
|
||||
y *= scale.y;
|
||||
z *= scale.z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes this vector.
|
||||
/// </summary>
|
||||
public void Normalize()
|
||||
{
|
||||
double mag = this.Magnitude;
|
||||
if (mag > Epsilon)
|
||||
{
|
||||
x /= mag;
|
||||
y /= mag;
|
||||
z /= mag;
|
||||
}
|
||||
else
|
||||
{
|
||||
x = y = z = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps this vector between a specific range.
|
||||
/// </summary>
|
||||
/// <param name="min">The minimum component value.</param>
|
||||
/// <param name="max">The maximum component value.</param>
|
||||
public void Clamp(double min, double max)
|
||||
{
|
||||
if (x < min) x = min;
|
||||
else if (x > max) x = max;
|
||||
|
||||
if (y < min) y = min;
|
||||
else if (y > max) y = max;
|
||||
|
||||
if (z < min) z = min;
|
||||
else if (z > max) z = max;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Object
|
||||
/// <summary>
|
||||
/// Returns a hash code for this vector.
|
||||
/// </summary>
|
||||
/// <returns>The hash code.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return x.GetHashCode() ^ y.GetHashCode() << 2 ^ z.GetHashCode() >> 2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if this vector is equal to another one.
|
||||
/// </summary>
|
||||
/// <param name="other">The other vector to compare to.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
if (!(other is Vector3d))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Vector3d vector = (Vector3d)other;
|
||||
return (x == vector.x && y == vector.y && z == vector.z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if this vector is equal to another one.
|
||||
/// </summary>
|
||||
/// <param name="other">The other vector to compare to.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public bool Equals(Vector3d other)
|
||||
{
|
||||
return (x == other.x && y == other.y && z == other.z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a nicely formatted string for this vector.
|
||||
/// </summary>
|
||||
/// <returns>The string.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("({0}, {1}, {2})",
|
||||
x.ToString("F1", CultureInfo.InvariantCulture),
|
||||
y.ToString("F1", CultureInfo.InvariantCulture),
|
||||
z.ToString("F1", CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a nicely formatted string for this vector.
|
||||
/// </summary>
|
||||
/// <param name="format">The float format.</param>
|
||||
/// <returns>The string.</returns>
|
||||
public string ToString(string format)
|
||||
{
|
||||
return string.Format("({0}, {1}, {2})",
|
||||
x.ToString(format, CultureInfo.InvariantCulture),
|
||||
y.ToString(format, CultureInfo.InvariantCulture),
|
||||
z.ToString(format, CultureInfo.InvariantCulture));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Static
|
||||
/// <summary>
|
||||
/// Dot Product of two vectors.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
public static double Dot(ref Vector3d lhs, ref Vector3d rhs)
|
||||
{
|
||||
return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cross Product of two vectors.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
/// <param name="result">The resulting vector.</param>
|
||||
public static void Cross(ref Vector3d lhs, ref Vector3d rhs, out Vector3d result)
|
||||
{
|
||||
result = new Vector3d(lhs.y * rhs.z - lhs.z * rhs.y, lhs.z * rhs.x - lhs.x * rhs.z, lhs.x * rhs.y - lhs.y * rhs.x);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the angle between two vectors.
|
||||
/// </summary>
|
||||
/// <param name="from">The from vector.</param>
|
||||
/// <param name="to">The to vector.</param>
|
||||
/// <returns>The angle.</returns>
|
||||
public static double Angle(ref Vector3d from, ref Vector3d to)
|
||||
{
|
||||
Vector3d fromNormalized = from.Normalized;
|
||||
Vector3d toNormalized = to.Normalized;
|
||||
return System.Math.Acos(MathHelper.Clamp(Vector3d.Dot(ref fromNormalized, ref toNormalized), -1.0, 1.0)) * MathHelper.Rad2Degd;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a linear interpolation between two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector to interpolate from.</param>
|
||||
/// <param name="b">The vector to interpolate to.</param>
|
||||
/// <param name="t">The time fraction.</param>
|
||||
/// <param name="result">The resulting vector.</param>
|
||||
public static void Lerp(ref Vector3d a, ref Vector3d b, double t, out Vector3d result)
|
||||
{
|
||||
result = new Vector3d(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies two vectors component-wise.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <param name="result">The resulting vector.</param>
|
||||
public static void Scale(ref Vector3d a, ref Vector3d b, out Vector3d result)
|
||||
{
|
||||
result = new Vector3d(a.x * b.x, a.y * b.y, a.z * b.z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes a vector.
|
||||
/// </summary>
|
||||
/// <param name="value">The vector to normalize.</param>
|
||||
/// <param name="result">The resulting normalized vector.</param>
|
||||
public static void Normalize(ref Vector3d value, out Vector3d result)
|
||||
{
|
||||
double mag = value.Magnitude;
|
||||
if (mag > Epsilon)
|
||||
{
|
||||
result = new Vector3d(value.x / mag, value.y / mag, value.z / mag);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = Vector3d.zero;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
368
LightlessSync/ThirdParty/MeshDecimator/Math/Vector3i.cs
vendored
Normal file
368
LightlessSync/ThirdParty/MeshDecimator/Math/Vector3i.cs
vendored
Normal file
@@ -0,0 +1,368 @@
|
||||
#region License
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright(c) 2017-2018 Mattias Edlund
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace MeshDecimator.Math
|
||||
{
|
||||
/// <summary>
|
||||
/// A 3D integer vector.
|
||||
/// </summary>
|
||||
public struct Vector3i : IEquatable<Vector3i>
|
||||
{
|
||||
#region Static Read-Only
|
||||
/// <summary>
|
||||
/// The zero vector.
|
||||
/// </summary>
|
||||
public static readonly Vector3i zero = new Vector3i(0, 0, 0);
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
/// <summary>
|
||||
/// The x component.
|
||||
/// </summary>
|
||||
public int x;
|
||||
/// <summary>
|
||||
/// The y component.
|
||||
/// </summary>
|
||||
public int y;
|
||||
/// <summary>
|
||||
/// The z component.
|
||||
/// </summary>
|
||||
public int z;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Gets the magnitude of this vector.
|
||||
/// </summary>
|
||||
public int Magnitude
|
||||
{
|
||||
get { return (int)System.Math.Sqrt(x * x + y * y + z * z); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the squared magnitude of this vector.
|
||||
/// </summary>
|
||||
public int MagnitudeSqr
|
||||
{
|
||||
get { return (x * x + y * y + z * z); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a specific component by index in this vector.
|
||||
/// </summary>
|
||||
/// <param name="index">The component index.</param>
|
||||
public int this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
return x;
|
||||
case 1:
|
||||
return y;
|
||||
case 2:
|
||||
return z;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector3i index!");
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
x = value;
|
||||
break;
|
||||
case 1:
|
||||
y = value;
|
||||
break;
|
||||
case 2:
|
||||
z = value;
|
||||
break;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector3i index!");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Creates a new vector with one value for all components.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
public Vector3i(int value)
|
||||
{
|
||||
this.x = value;
|
||||
this.y = value;
|
||||
this.z = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new vector.
|
||||
/// </summary>
|
||||
/// <param name="x">The x value.</param>
|
||||
/// <param name="y">The y value.</param>
|
||||
/// <param name="z">The z value.</param>
|
||||
public Vector3i(int x, int y, int z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
/// <summary>
|
||||
/// Adds two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector3i operator +(Vector3i a, Vector3i b)
|
||||
{
|
||||
return new Vector3i(a.x + b.x, a.y + b.y, a.z + b.z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector3i operator -(Vector3i a, Vector3i b)
|
||||
{
|
||||
return new Vector3i(a.x - b.x, a.y - b.y, a.z - b.z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales the vector uniformly.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <param name="d">The scaling value.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector3i operator *(Vector3i a, int d)
|
||||
{
|
||||
return new Vector3i(a.x * d, a.y * d, a.z * d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales the vector uniformly.
|
||||
/// </summary>
|
||||
/// <param name="d">The scaling value.</param>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector3i operator *(int d, Vector3i a)
|
||||
{
|
||||
return new Vector3i(a.x * d, a.y * d, a.z * d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides the vector with a float.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <param name="d">The dividing float value.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector3i operator /(Vector3i a, int d)
|
||||
{
|
||||
return new Vector3i(a.x / d, a.y / d, a.z / d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts the vector from a zero vector.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector3i operator -(Vector3i a)
|
||||
{
|
||||
return new Vector3i(-a.x, -a.y, -a.z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if two vectors equals eachother.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public static bool operator ==(Vector3i lhs, Vector3i rhs)
|
||||
{
|
||||
return (lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if two vectors don't equal eachother.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
/// <returns>If not equals.</returns>
|
||||
public static bool operator !=(Vector3i lhs, Vector3i rhs)
|
||||
{
|
||||
return (lhs.x != rhs.x || lhs.y != rhs.y || lhs.z != rhs.z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicitly converts from a single-precision vector into an integer vector.
|
||||
/// </summary>
|
||||
/// <param name="v">The single-precision vector.</param>
|
||||
public static implicit operator Vector3i(Vector3 v)
|
||||
{
|
||||
return new Vector3i((int)v.x, (int)v.y, (int)v.z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicitly converts from a double-precision vector into an integer vector.
|
||||
/// </summary>
|
||||
/// <param name="v">The double-precision vector.</param>
|
||||
public static explicit operator Vector3i(Vector3d v)
|
||||
{
|
||||
return new Vector3i((int)v.x, (int)v.y, (int)v.z);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
#region Instance
|
||||
/// <summary>
|
||||
/// Set x, y and z components of an existing vector.
|
||||
/// </summary>
|
||||
/// <param name="x">The x value.</param>
|
||||
/// <param name="y">The y value.</param>
|
||||
/// <param name="z">The z value.</param>
|
||||
public void Set(int x, int y, int z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies with another vector component-wise.
|
||||
/// </summary>
|
||||
/// <param name="scale">The vector to multiply with.</param>
|
||||
public void Scale(ref Vector3i scale)
|
||||
{
|
||||
x *= scale.x;
|
||||
y *= scale.y;
|
||||
z *= scale.z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps this vector between a specific range.
|
||||
/// </summary>
|
||||
/// <param name="min">The minimum component value.</param>
|
||||
/// <param name="max">The maximum component value.</param>
|
||||
public void Clamp(int min, int max)
|
||||
{
|
||||
if (x < min) x = min;
|
||||
else if (x > max) x = max;
|
||||
|
||||
if (y < min) y = min;
|
||||
else if (y > max) y = max;
|
||||
|
||||
if (z < min) z = min;
|
||||
else if (z > max) z = max;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Object
|
||||
/// <summary>
|
||||
/// Returns a hash code for this vector.
|
||||
/// </summary>
|
||||
/// <returns>The hash code.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return x.GetHashCode() ^ y.GetHashCode() << 2 ^ z.GetHashCode() >> 2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if this vector is equal to another one.
|
||||
/// </summary>
|
||||
/// <param name="other">The other vector to compare to.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
if (!(other is Vector3i))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Vector3i vector = (Vector3i)other;
|
||||
return (x == vector.x && y == vector.y && z == vector.z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if this vector is equal to another one.
|
||||
/// </summary>
|
||||
/// <param name="other">The other vector to compare to.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public bool Equals(Vector3i other)
|
||||
{
|
||||
return (x == other.x && y == other.y && z == other.z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a nicely formatted string for this vector.
|
||||
/// </summary>
|
||||
/// <returns>The string.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("({0}, {1}, {2})",
|
||||
x.ToString(CultureInfo.InvariantCulture),
|
||||
y.ToString(CultureInfo.InvariantCulture),
|
||||
z.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a nicely formatted string for this vector.
|
||||
/// </summary>
|
||||
/// <param name="format">The integer format.</param>
|
||||
/// <returns>The string.</returns>
|
||||
public string ToString(string format)
|
||||
{
|
||||
return string.Format("({0}, {1}, {2})",
|
||||
x.ToString(format, CultureInfo.InvariantCulture),
|
||||
y.ToString(format, CultureInfo.InvariantCulture),
|
||||
z.ToString(format, CultureInfo.InvariantCulture));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Static
|
||||
/// <summary>
|
||||
/// Multiplies two vectors component-wise.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <param name="result">The resulting vector.</param>
|
||||
public static void Scale(ref Vector3i a, ref Vector3i b, out Vector3i result)
|
||||
{
|
||||
result = new Vector3i(a.x * b.x, a.y * b.y, a.z * b.z);
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
467
LightlessSync/ThirdParty/MeshDecimator/Math/Vector4.cs
vendored
Normal file
467
LightlessSync/ThirdParty/MeshDecimator/Math/Vector4.cs
vendored
Normal file
@@ -0,0 +1,467 @@
|
||||
#region License
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright(c) 2017-2018 Mattias Edlund
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace MeshDecimator.Math
|
||||
{
|
||||
/// <summary>
|
||||
/// A single precision 4D vector.
|
||||
/// </summary>
|
||||
public struct Vector4 : IEquatable<Vector4>
|
||||
{
|
||||
#region Static Read-Only
|
||||
/// <summary>
|
||||
/// The zero vector.
|
||||
/// </summary>
|
||||
public static readonly Vector4 zero = new Vector4(0, 0, 0, 0);
|
||||
#endregion
|
||||
|
||||
#region Consts
|
||||
/// <summary>
|
||||
/// The vector epsilon.
|
||||
/// </summary>
|
||||
public const float Epsilon = 9.99999944E-11f;
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
/// <summary>
|
||||
/// The x component.
|
||||
/// </summary>
|
||||
public float x;
|
||||
/// <summary>
|
||||
/// The y component.
|
||||
/// </summary>
|
||||
public float y;
|
||||
/// <summary>
|
||||
/// The z component.
|
||||
/// </summary>
|
||||
public float z;
|
||||
/// <summary>
|
||||
/// The w component.
|
||||
/// </summary>
|
||||
public float w;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Gets the magnitude of this vector.
|
||||
/// </summary>
|
||||
public float Magnitude
|
||||
{
|
||||
get { return (float)System.Math.Sqrt(x * x + y * y + z * z + w * w); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the squared magnitude of this vector.
|
||||
/// </summary>
|
||||
public float MagnitudeSqr
|
||||
{
|
||||
get { return (x * x + y * y + z * z + w * w); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a normalized vector from this vector.
|
||||
/// </summary>
|
||||
public Vector4 Normalized
|
||||
{
|
||||
get
|
||||
{
|
||||
Vector4 result;
|
||||
Normalize(ref this, out result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a specific component by index in this vector.
|
||||
/// </summary>
|
||||
/// <param name="index">The component index.</param>
|
||||
public float this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
return x;
|
||||
case 1:
|
||||
return y;
|
||||
case 2:
|
||||
return z;
|
||||
case 3:
|
||||
return w;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector4 index!");
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
x = value;
|
||||
break;
|
||||
case 1:
|
||||
y = value;
|
||||
break;
|
||||
case 2:
|
||||
z = value;
|
||||
break;
|
||||
case 3:
|
||||
w = value;
|
||||
break;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector4 index!");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Creates a new vector with one value for all components.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
public Vector4(float value)
|
||||
{
|
||||
this.x = value;
|
||||
this.y = value;
|
||||
this.z = value;
|
||||
this.w = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new vector.
|
||||
/// </summary>
|
||||
/// <param name="x">The x value.</param>
|
||||
/// <param name="y">The y value.</param>
|
||||
/// <param name="z">The z value.</param>
|
||||
/// <param name="w">The w value.</param>
|
||||
public Vector4(float x, float y, float z, float w)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.w = w;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
/// <summary>
|
||||
/// Adds two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector4 operator +(Vector4 a, Vector4 b)
|
||||
{
|
||||
return new Vector4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector4 operator -(Vector4 a, Vector4 b)
|
||||
{
|
||||
return new Vector4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales the vector uniformly.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <param name="d">The scaling value.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector4 operator *(Vector4 a, float d)
|
||||
{
|
||||
return new Vector4(a.x * d, a.y * d, a.z * d, a.w * d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales the vector uniformly.
|
||||
/// </summary>
|
||||
/// <param name="d">The scaling value.</param>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector4 operator *(float d, Vector4 a)
|
||||
{
|
||||
return new Vector4(a.x * d, a.y * d, a.z * d, a.w * d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides the vector with a float.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <param name="d">The dividing float value.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector4 operator /(Vector4 a, float d)
|
||||
{
|
||||
return new Vector4(a.x / d, a.y / d, a.z / d, a.w / d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts the vector from a zero vector.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector4 operator -(Vector4 a)
|
||||
{
|
||||
return new Vector4(-a.x, -a.y, -a.z, -a.w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if two vectors equals eachother.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public static bool operator ==(Vector4 lhs, Vector4 rhs)
|
||||
{
|
||||
return (lhs - rhs).MagnitudeSqr < Epsilon;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if two vectors don't equal eachother.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
/// <returns>If not equals.</returns>
|
||||
public static bool operator !=(Vector4 lhs, Vector4 rhs)
|
||||
{
|
||||
return (lhs - rhs).MagnitudeSqr >= Epsilon;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicitly converts from a double-precision vector into a single-precision vector.
|
||||
/// </summary>
|
||||
/// <param name="v">The double-precision vector.</param>
|
||||
public static explicit operator Vector4(Vector4d v)
|
||||
{
|
||||
return new Vector4((float)v.x, (float)v.y, (float)v.z, (float)v.w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly converts from an integer vector into a single-precision vector.
|
||||
/// </summary>
|
||||
/// <param name="v">The integer vector.</param>
|
||||
public static implicit operator Vector4(Vector4i v)
|
||||
{
|
||||
return new Vector4(v.x, v.y, v.z, v.w);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
#region Instance
|
||||
/// <summary>
|
||||
/// Set x, y and z components of an existing vector.
|
||||
/// </summary>
|
||||
/// <param name="x">The x value.</param>
|
||||
/// <param name="y">The y value.</param>
|
||||
/// <param name="z">The z value.</param>
|
||||
/// <param name="w">The w value.</param>
|
||||
public void Set(float x, float y, float z, float w)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.w = w;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies with another vector component-wise.
|
||||
/// </summary>
|
||||
/// <param name="scale">The vector to multiply with.</param>
|
||||
public void Scale(ref Vector4 scale)
|
||||
{
|
||||
x *= scale.x;
|
||||
y *= scale.y;
|
||||
z *= scale.z;
|
||||
w *= scale.w;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes this vector.
|
||||
/// </summary>
|
||||
public void Normalize()
|
||||
{
|
||||
float mag = this.Magnitude;
|
||||
if (mag > Epsilon)
|
||||
{
|
||||
x /= mag;
|
||||
y /= mag;
|
||||
z /= mag;
|
||||
w /= mag;
|
||||
}
|
||||
else
|
||||
{
|
||||
x = y = z = w = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps this vector between a specific range.
|
||||
/// </summary>
|
||||
/// <param name="min">The minimum component value.</param>
|
||||
/// <param name="max">The maximum component value.</param>
|
||||
public void Clamp(float min, float max)
|
||||
{
|
||||
if (x < min) x = min;
|
||||
else if (x > max) x = max;
|
||||
|
||||
if (y < min) y = min;
|
||||
else if (y > max) y = max;
|
||||
|
||||
if (z < min) z = min;
|
||||
else if (z > max) z = max;
|
||||
|
||||
if (w < min) w = min;
|
||||
else if (w > max) w = max;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Object
|
||||
/// <summary>
|
||||
/// Returns a hash code for this vector.
|
||||
/// </summary>
|
||||
/// <returns>The hash code.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return x.GetHashCode() ^ y.GetHashCode() << 2 ^ z.GetHashCode() >> 2 ^ w.GetHashCode() >> 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if this vector is equal to another one.
|
||||
/// </summary>
|
||||
/// <param name="other">The other vector to compare to.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
if (!(other is Vector4))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Vector4 vector = (Vector4)other;
|
||||
return (x == vector.x && y == vector.y && z == vector.z && w == vector.w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if this vector is equal to another one.
|
||||
/// </summary>
|
||||
/// <param name="other">The other vector to compare to.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public bool Equals(Vector4 other)
|
||||
{
|
||||
return (x == other.x && y == other.y && z == other.z && w == other.w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a nicely formatted string for this vector.
|
||||
/// </summary>
|
||||
/// <returns>The string.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("({0}, {1}, {2}, {3})",
|
||||
x.ToString("F1", CultureInfo.InvariantCulture),
|
||||
y.ToString("F1", CultureInfo.InvariantCulture),
|
||||
z.ToString("F1", CultureInfo.InvariantCulture),
|
||||
w.ToString("F1", CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a nicely formatted string for this vector.
|
||||
/// </summary>
|
||||
/// <param name="format">The float format.</param>
|
||||
/// <returns>The string.</returns>
|
||||
public string ToString(string format)
|
||||
{
|
||||
return string.Format("({0}, {1}, {2}, {3})",
|
||||
x.ToString(format, CultureInfo.InvariantCulture),
|
||||
y.ToString(format, CultureInfo.InvariantCulture),
|
||||
z.ToString(format, CultureInfo.InvariantCulture),
|
||||
w.ToString(format, CultureInfo.InvariantCulture));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Static
|
||||
/// <summary>
|
||||
/// Dot Product of two vectors.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
public static float Dot(ref Vector4 lhs, ref Vector4 rhs)
|
||||
{
|
||||
return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z + lhs.w * rhs.w;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a linear interpolation between two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector to interpolate from.</param>
|
||||
/// <param name="b">The vector to interpolate to.</param>
|
||||
/// <param name="t">The time fraction.</param>
|
||||
/// <param name="result">The resulting vector.</param>
|
||||
public static void Lerp(ref Vector4 a, ref Vector4 b, float t, out Vector4 result)
|
||||
{
|
||||
result = new Vector4(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t, a.w + (b.w - a.w) * t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies two vectors component-wise.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <param name="result">The resulting vector.</param>
|
||||
public static void Scale(ref Vector4 a, ref Vector4 b, out Vector4 result)
|
||||
{
|
||||
result = new Vector4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes a vector.
|
||||
/// </summary>
|
||||
/// <param name="value">The vector to normalize.</param>
|
||||
/// <param name="result">The resulting normalized vector.</param>
|
||||
public static void Normalize(ref Vector4 value, out Vector4 result)
|
||||
{
|
||||
float mag = value.Magnitude;
|
||||
if (mag > Epsilon)
|
||||
{
|
||||
result = new Vector4(value.x / mag, value.y / mag, value.z / mag, value.w / mag);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = Vector4.zero;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
467
LightlessSync/ThirdParty/MeshDecimator/Math/Vector4d.cs
vendored
Normal file
467
LightlessSync/ThirdParty/MeshDecimator/Math/Vector4d.cs
vendored
Normal file
@@ -0,0 +1,467 @@
|
||||
#region License
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright(c) 2017-2018 Mattias Edlund
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace MeshDecimator.Math
|
||||
{
|
||||
/// <summary>
|
||||
/// A double precision 4D vector.
|
||||
/// </summary>
|
||||
public struct Vector4d : IEquatable<Vector4d>
|
||||
{
|
||||
#region Static Read-Only
|
||||
/// <summary>
|
||||
/// The zero vector.
|
||||
/// </summary>
|
||||
public static readonly Vector4d zero = new Vector4d(0, 0, 0, 0);
|
||||
#endregion
|
||||
|
||||
#region Consts
|
||||
/// <summary>
|
||||
/// The vector epsilon.
|
||||
/// </summary>
|
||||
public const double Epsilon = double.Epsilon;
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
/// <summary>
|
||||
/// The x component.
|
||||
/// </summary>
|
||||
public double x;
|
||||
/// <summary>
|
||||
/// The y component.
|
||||
/// </summary>
|
||||
public double y;
|
||||
/// <summary>
|
||||
/// The z component.
|
||||
/// </summary>
|
||||
public double z;
|
||||
/// <summary>
|
||||
/// The w component.
|
||||
/// </summary>
|
||||
public double w;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Gets the magnitude of this vector.
|
||||
/// </summary>
|
||||
public double Magnitude
|
||||
{
|
||||
get { return System.Math.Sqrt(x * x + y * y + z * z + w * w); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the squared magnitude of this vector.
|
||||
/// </summary>
|
||||
public double MagnitudeSqr
|
||||
{
|
||||
get { return (x * x + y * y + z * z + w * w); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a normalized vector from this vector.
|
||||
/// </summary>
|
||||
public Vector4d Normalized
|
||||
{
|
||||
get
|
||||
{
|
||||
Vector4d result;
|
||||
Normalize(ref this, out result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a specific component by index in this vector.
|
||||
/// </summary>
|
||||
/// <param name="index">The component index.</param>
|
||||
public double this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
return x;
|
||||
case 1:
|
||||
return y;
|
||||
case 2:
|
||||
return z;
|
||||
case 3:
|
||||
return w;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector4d index!");
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
x = value;
|
||||
break;
|
||||
case 1:
|
||||
y = value;
|
||||
break;
|
||||
case 2:
|
||||
z = value;
|
||||
break;
|
||||
case 3:
|
||||
w = value;
|
||||
break;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector4d index!");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Creates a new vector with one value for all components.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
public Vector4d(double value)
|
||||
{
|
||||
this.x = value;
|
||||
this.y = value;
|
||||
this.z = value;
|
||||
this.w = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new vector.
|
||||
/// </summary>
|
||||
/// <param name="x">The x value.</param>
|
||||
/// <param name="y">The y value.</param>
|
||||
/// <param name="z">The z value.</param>
|
||||
/// <param name="w">The w value.</param>
|
||||
public Vector4d(double x, double y, double z, double w)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.w = w;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
/// <summary>
|
||||
/// Adds two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector4d operator +(Vector4d a, Vector4d b)
|
||||
{
|
||||
return new Vector4d(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector4d operator -(Vector4d a, Vector4d b)
|
||||
{
|
||||
return new Vector4d(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales the vector uniformly.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <param name="d">The scaling value.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector4d operator *(Vector4d a, double d)
|
||||
{
|
||||
return new Vector4d(a.x * d, a.y * d, a.z * d, a.w * d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales the vector uniformly.
|
||||
/// </summary>
|
||||
/// <param name="d">The scaling value.</param>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector4d operator *(double d, Vector4d a)
|
||||
{
|
||||
return new Vector4d(a.x * d, a.y * d, a.z * d, a.w * d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides the vector with a float.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <param name="d">The dividing float value.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector4d operator /(Vector4d a, double d)
|
||||
{
|
||||
return new Vector4d(a.x / d, a.y / d, a.z / d, a.w / d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts the vector from a zero vector.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector4d operator -(Vector4d a)
|
||||
{
|
||||
return new Vector4d(-a.x, -a.y, -a.z, -a.w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if two vectors equals eachother.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public static bool operator ==(Vector4d lhs, Vector4d rhs)
|
||||
{
|
||||
return (lhs - rhs).MagnitudeSqr < Epsilon;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if two vectors don't equal eachother.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
/// <returns>If not equals.</returns>
|
||||
public static bool operator !=(Vector4d lhs, Vector4d rhs)
|
||||
{
|
||||
return (lhs - rhs).MagnitudeSqr >= Epsilon;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly converts from a single-precision vector into a double-precision vector.
|
||||
/// </summary>
|
||||
/// <param name="v">The single-precision vector.</param>
|
||||
public static implicit operator Vector4d(Vector4 v)
|
||||
{
|
||||
return new Vector4d(v.x, v.y, v.z, v.w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly converts from an integer vector into a double-precision vector.
|
||||
/// </summary>
|
||||
/// <param name="v">The integer vector.</param>
|
||||
public static implicit operator Vector4d(Vector4i v)
|
||||
{
|
||||
return new Vector4d(v.x, v.y, v.z, v.w);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
#region Instance
|
||||
/// <summary>
|
||||
/// Set x, y and z components of an existing vector.
|
||||
/// </summary>
|
||||
/// <param name="x">The x value.</param>
|
||||
/// <param name="y">The y value.</param>
|
||||
/// <param name="z">The z value.</param>
|
||||
/// <param name="w">The w value.</param>
|
||||
public void Set(double x, double y, double z, double w)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.w = w;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies with another vector component-wise.
|
||||
/// </summary>
|
||||
/// <param name="scale">The vector to multiply with.</param>
|
||||
public void Scale(ref Vector4d scale)
|
||||
{
|
||||
x *= scale.x;
|
||||
y *= scale.y;
|
||||
z *= scale.z;
|
||||
w *= scale.w;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes this vector.
|
||||
/// </summary>
|
||||
public void Normalize()
|
||||
{
|
||||
double mag = this.Magnitude;
|
||||
if (mag > Epsilon)
|
||||
{
|
||||
x /= mag;
|
||||
y /= mag;
|
||||
z /= mag;
|
||||
w /= mag;
|
||||
}
|
||||
else
|
||||
{
|
||||
x = y = z = w = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps this vector between a specific range.
|
||||
/// </summary>
|
||||
/// <param name="min">The minimum component value.</param>
|
||||
/// <param name="max">The maximum component value.</param>
|
||||
public void Clamp(double min, double max)
|
||||
{
|
||||
if (x < min) x = min;
|
||||
else if (x > max) x = max;
|
||||
|
||||
if (y < min) y = min;
|
||||
else if (y > max) y = max;
|
||||
|
||||
if (z < min) z = min;
|
||||
else if (z > max) z = max;
|
||||
|
||||
if (w < min) w = min;
|
||||
else if (w > max) w = max;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Object
|
||||
/// <summary>
|
||||
/// Returns a hash code for this vector.
|
||||
/// </summary>
|
||||
/// <returns>The hash code.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return x.GetHashCode() ^ y.GetHashCode() << 2 ^ z.GetHashCode() >> 2 ^ w.GetHashCode() >> 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if this vector is equal to another one.
|
||||
/// </summary>
|
||||
/// <param name="other">The other vector to compare to.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
if (!(other is Vector4d))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Vector4d vector = (Vector4d)other;
|
||||
return (x == vector.x && y == vector.y && z == vector.z && w == vector.w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if this vector is equal to another one.
|
||||
/// </summary>
|
||||
/// <param name="other">The other vector to compare to.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public bool Equals(Vector4d other)
|
||||
{
|
||||
return (x == other.x && y == other.y && z == other.z && w == other.w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a nicely formatted string for this vector.
|
||||
/// </summary>
|
||||
/// <returns>The string.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("({0}, {1}, {2}, {3})",
|
||||
x.ToString("F1", CultureInfo.InvariantCulture),
|
||||
y.ToString("F1", CultureInfo.InvariantCulture),
|
||||
z.ToString("F1", CultureInfo.InvariantCulture),
|
||||
w.ToString("F1", CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a nicely formatted string for this vector.
|
||||
/// </summary>
|
||||
/// <param name="format">The float format.</param>
|
||||
/// <returns>The string.</returns>
|
||||
public string ToString(string format)
|
||||
{
|
||||
return string.Format("({0}, {1}, {2}, {3})",
|
||||
x.ToString(format, CultureInfo.InvariantCulture),
|
||||
y.ToString(format, CultureInfo.InvariantCulture),
|
||||
z.ToString(format, CultureInfo.InvariantCulture),
|
||||
w.ToString(format, CultureInfo.InvariantCulture));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Static
|
||||
/// <summary>
|
||||
/// Dot Product of two vectors.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
public static double Dot(ref Vector4d lhs, ref Vector4d rhs)
|
||||
{
|
||||
return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z + lhs.w * rhs.w;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a linear interpolation between two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector to interpolate from.</param>
|
||||
/// <param name="b">The vector to interpolate to.</param>
|
||||
/// <param name="t">The time fraction.</param>
|
||||
/// <param name="result">The resulting vector.</param>
|
||||
public static void Lerp(ref Vector4d a, ref Vector4d b, double t, out Vector4d result)
|
||||
{
|
||||
result = new Vector4d(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t, a.w + (b.w - a.w) * t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies two vectors component-wise.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <param name="result">The resulting vector.</param>
|
||||
public static void Scale(ref Vector4d a, ref Vector4d b, out Vector4d result)
|
||||
{
|
||||
result = new Vector4d(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes a vector.
|
||||
/// </summary>
|
||||
/// <param name="value">The vector to normalize.</param>
|
||||
/// <param name="result">The resulting normalized vector.</param>
|
||||
public static void Normalize(ref Vector4d value, out Vector4d result)
|
||||
{
|
||||
double mag = value.Magnitude;
|
||||
if (mag > Epsilon)
|
||||
{
|
||||
result = new Vector4d(value.x / mag, value.y / mag, value.z / mag, value.w / mag);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = Vector4d.zero;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
388
LightlessSync/ThirdParty/MeshDecimator/Math/Vector4i.cs
vendored
Normal file
388
LightlessSync/ThirdParty/MeshDecimator/Math/Vector4i.cs
vendored
Normal file
@@ -0,0 +1,388 @@
|
||||
#region License
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright(c) 2017-2018 Mattias Edlund
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace MeshDecimator.Math
|
||||
{
|
||||
/// <summary>
|
||||
/// A 4D integer vector.
|
||||
/// </summary>
|
||||
public struct Vector4i : IEquatable<Vector4i>
|
||||
{
|
||||
#region Static Read-Only
|
||||
/// <summary>
|
||||
/// The zero vector.
|
||||
/// </summary>
|
||||
public static readonly Vector4i zero = new Vector4i(0, 0, 0, 0);
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
/// <summary>
|
||||
/// The x component.
|
||||
/// </summary>
|
||||
public int x;
|
||||
/// <summary>
|
||||
/// The y component.
|
||||
/// </summary>
|
||||
public int y;
|
||||
/// <summary>
|
||||
/// The z component.
|
||||
/// </summary>
|
||||
public int z;
|
||||
/// <summary>
|
||||
/// The w component.
|
||||
/// </summary>
|
||||
public int w;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Gets the magnitude of this vector.
|
||||
/// </summary>
|
||||
public int Magnitude
|
||||
{
|
||||
get { return (int)System.Math.Sqrt(x * x + y * y + z * z + w * w); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the squared magnitude of this vector.
|
||||
/// </summary>
|
||||
public int MagnitudeSqr
|
||||
{
|
||||
get { return (x * x + y * y + z * z + w * w); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a specific component by index in this vector.
|
||||
/// </summary>
|
||||
/// <param name="index">The component index.</param>
|
||||
public int this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
return x;
|
||||
case 1:
|
||||
return y;
|
||||
case 2:
|
||||
return z;
|
||||
case 3:
|
||||
return w;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector4i index!");
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
x = value;
|
||||
break;
|
||||
case 1:
|
||||
y = value;
|
||||
break;
|
||||
case 2:
|
||||
z = value;
|
||||
break;
|
||||
case 3:
|
||||
w = value;
|
||||
break;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector4i index!");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Creates a new vector with one value for all components.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
public Vector4i(int value)
|
||||
{
|
||||
this.x = value;
|
||||
this.y = value;
|
||||
this.z = value;
|
||||
this.w = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new vector.
|
||||
/// </summary>
|
||||
/// <param name="x">The x value.</param>
|
||||
/// <param name="y">The y value.</param>
|
||||
/// <param name="z">The z value.</param>
|
||||
/// <param name="w">The w value.</param>
|
||||
public Vector4i(int x, int y, int z, int w)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.w = w;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
/// <summary>
|
||||
/// Adds two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector4i operator +(Vector4i a, Vector4i b)
|
||||
{
|
||||
return new Vector4i(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector4i operator -(Vector4i a, Vector4i b)
|
||||
{
|
||||
return new Vector4i(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales the vector uniformly.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <param name="d">The scaling value.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector4i operator *(Vector4i a, int d)
|
||||
{
|
||||
return new Vector4i(a.x * d, a.y * d, a.z * d, a.w * d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales the vector uniformly.
|
||||
/// </summary>
|
||||
/// <param name="d">The scaling value.</param>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector4i operator *(int d, Vector4i a)
|
||||
{
|
||||
return new Vector4i(a.x * d, a.y * d, a.z * d, a.w * d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides the vector with a float.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <param name="d">The dividing float value.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector4i operator /(Vector4i a, int d)
|
||||
{
|
||||
return new Vector4i(a.x / d, a.y / d, a.z / d, a.w / d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts the vector from a zero vector.
|
||||
/// </summary>
|
||||
/// <param name="a">The vector.</param>
|
||||
/// <returns>The resulting vector.</returns>
|
||||
public static Vector4i operator -(Vector4i a)
|
||||
{
|
||||
return new Vector4i(-a.x, -a.y, -a.z, -a.w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if two vectors equals eachother.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public static bool operator ==(Vector4i lhs, Vector4i rhs)
|
||||
{
|
||||
return (lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z && lhs.w == rhs.w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if two vectors don't equal eachother.
|
||||
/// </summary>
|
||||
/// <param name="lhs">The left hand side vector.</param>
|
||||
/// <param name="rhs">The right hand side vector.</param>
|
||||
/// <returns>If not equals.</returns>
|
||||
public static bool operator !=(Vector4i lhs, Vector4i rhs)
|
||||
{
|
||||
return (lhs.x != rhs.x || lhs.y != rhs.y || lhs.z != rhs.z || lhs.w != rhs.w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicitly converts from a single-precision vector into an integer vector.
|
||||
/// </summary>
|
||||
/// <param name="v">The single-precision vector.</param>
|
||||
public static explicit operator Vector4i(Vector4 v)
|
||||
{
|
||||
return new Vector4i((int)v.x, (int)v.y, (int)v.z, (int)v.w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicitly converts from a double-precision vector into an integer vector.
|
||||
/// </summary>
|
||||
/// <param name="v">The double-precision vector.</param>
|
||||
public static explicit operator Vector4i(Vector4d v)
|
||||
{
|
||||
return new Vector4i((int)v.x, (int)v.y, (int)v.z, (int)v.w);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
#region Instance
|
||||
/// <summary>
|
||||
/// Set x, y and z components of an existing vector.
|
||||
/// </summary>
|
||||
/// <param name="x">The x value.</param>
|
||||
/// <param name="y">The y value.</param>
|
||||
/// <param name="z">The z value.</param>
|
||||
/// <param name="w">The w value.</param>
|
||||
public void Set(int x, int y, int z, int w)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.w = w;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies with another vector component-wise.
|
||||
/// </summary>
|
||||
/// <param name="scale">The vector to multiply with.</param>
|
||||
public void Scale(ref Vector4i scale)
|
||||
{
|
||||
x *= scale.x;
|
||||
y *= scale.y;
|
||||
z *= scale.z;
|
||||
w *= scale.w;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps this vector between a specific range.
|
||||
/// </summary>
|
||||
/// <param name="min">The minimum component value.</param>
|
||||
/// <param name="max">The maximum component value.</param>
|
||||
public void Clamp(int min, int max)
|
||||
{
|
||||
if (x < min) x = min;
|
||||
else if (x > max) x = max;
|
||||
|
||||
if (y < min) y = min;
|
||||
else if (y > max) y = max;
|
||||
|
||||
if (z < min) z = min;
|
||||
else if (z > max) z = max;
|
||||
|
||||
if (w < min) w = min;
|
||||
else if (w > max) w = max;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Object
|
||||
/// <summary>
|
||||
/// Returns a hash code for this vector.
|
||||
/// </summary>
|
||||
/// <returns>The hash code.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return x.GetHashCode() ^ y.GetHashCode() << 2 ^ z.GetHashCode() >> 2 ^ w.GetHashCode() >> 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if this vector is equal to another one.
|
||||
/// </summary>
|
||||
/// <param name="other">The other vector to compare to.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
if (!(other is Vector4i))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Vector4i vector = (Vector4i)other;
|
||||
return (x == vector.x && y == vector.y && z == vector.z && w == vector.w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if this vector is equal to another one.
|
||||
/// </summary>
|
||||
/// <param name="other">The other vector to compare to.</param>
|
||||
/// <returns>If equals.</returns>
|
||||
public bool Equals(Vector4i other)
|
||||
{
|
||||
return (x == other.x && y == other.y && z == other.z && w == other.w);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a nicely formatted string for this vector.
|
||||
/// </summary>
|
||||
/// <returns>The string.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("({0}, {1}, {2}, {3})",
|
||||
x.ToString(CultureInfo.InvariantCulture),
|
||||
y.ToString(CultureInfo.InvariantCulture),
|
||||
z.ToString(CultureInfo.InvariantCulture),
|
||||
w.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a nicely formatted string for this vector.
|
||||
/// </summary>
|
||||
/// <param name="format">The integer format.</param>
|
||||
/// <returns>The string.</returns>
|
||||
public string ToString(string format)
|
||||
{
|
||||
return string.Format("({0}, {1}, {2}, {3})",
|
||||
x.ToString(format, CultureInfo.InvariantCulture),
|
||||
y.ToString(format, CultureInfo.InvariantCulture),
|
||||
z.ToString(format, CultureInfo.InvariantCulture),
|
||||
w.ToString(format, CultureInfo.InvariantCulture));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Static
|
||||
/// <summary>
|
||||
/// Multiplies two vectors component-wise.
|
||||
/// </summary>
|
||||
/// <param name="a">The first vector.</param>
|
||||
/// <param name="b">The second vector.</param>
|
||||
/// <param name="result">The resulting vector.</param>
|
||||
public static void Scale(ref Vector4i a, ref Vector4i b, out Vector4i result)
|
||||
{
|
||||
result = new Vector4i(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
955
LightlessSync/ThirdParty/MeshDecimator/Mesh.cs
vendored
Normal file
955
LightlessSync/ThirdParty/MeshDecimator/Mesh.cs
vendored
Normal file
@@ -0,0 +1,955 @@
|
||||
#region License
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright(c) 2017-2018 Mattias Edlund
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MeshDecimator.Math;
|
||||
|
||||
namespace MeshDecimator
|
||||
{
|
||||
/// <summary>
|
||||
/// A mesh.
|
||||
/// </summary>
|
||||
public sealed class Mesh
|
||||
{
|
||||
#region Consts
|
||||
/// <summary>
|
||||
/// The count of supported UV channels.
|
||||
/// </summary>
|
||||
public const int UVChannelCount = 4;
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private Vector3d[] vertices = null;
|
||||
private int[][] indices = null;
|
||||
private Vector3[] normals = null;
|
||||
private Vector4[] tangents = null;
|
||||
private Vector2[][] uvs2D = null;
|
||||
private Vector3[][] uvs3D = null;
|
||||
private Vector4[][] uvs4D = null;
|
||||
private Vector4[] colors = null;
|
||||
private BoneWeight[] boneWeights = null;
|
||||
|
||||
private static readonly int[] emptyIndices = new int[0];
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Gets the count of vertices of this mesh.
|
||||
/// </summary>
|
||||
public int VertexCount
|
||||
{
|
||||
get { return vertices.Length; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the count of submeshes in this mesh.
|
||||
/// </summary>
|
||||
public int SubMeshCount
|
||||
{
|
||||
get { return indices.Length; }
|
||||
set
|
||||
{
|
||||
if (value <= 0)
|
||||
throw new ArgumentOutOfRangeException("value");
|
||||
|
||||
int[][] newIndices = new int[value][];
|
||||
Array.Copy(indices, 0, newIndices, 0, MathHelper.Min(indices.Length, newIndices.Length));
|
||||
indices = newIndices;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total count of triangles in this mesh.
|
||||
/// </summary>
|
||||
public int TriangleCount
|
||||
{
|
||||
get
|
||||
{
|
||||
int triangleCount = 0;
|
||||
for (int i = 0; i < indices.Length; i++)
|
||||
{
|
||||
if (indices[i] != null)
|
||||
{
|
||||
triangleCount += indices[i].Length / 3;
|
||||
}
|
||||
}
|
||||
return triangleCount;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the vertices for this mesh. Note that this resets all other vertex attributes.
|
||||
/// </summary>
|
||||
public Vector3d[] Vertices
|
||||
{
|
||||
get { return vertices; }
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
vertices = value;
|
||||
ClearVertexAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the combined indices for this mesh. Once set, the sub-mesh count gets set to 1.
|
||||
/// </summary>
|
||||
public int[] Indices
|
||||
{
|
||||
get
|
||||
{
|
||||
if (indices.Length == 1)
|
||||
{
|
||||
return indices[0] ?? emptyIndices;
|
||||
}
|
||||
else
|
||||
{
|
||||
List<int> indexList = new List<int>(TriangleCount * 3);
|
||||
for (int i = 0; i < indices.Length; i++)
|
||||
{
|
||||
if (indices[i] != null)
|
||||
{
|
||||
indexList.AddRange(indices[i]);
|
||||
}
|
||||
}
|
||||
return indexList.ToArray();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
else if ((value.Length % 3) != 0)
|
||||
throw new ArgumentException("The index count must be multiple by 3.", "value");
|
||||
|
||||
SubMeshCount = 1;
|
||||
SetIndices(0, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the normals for this mesh.
|
||||
/// </summary>
|
||||
public Vector3[] Normals
|
||||
{
|
||||
get { return normals; }
|
||||
set
|
||||
{
|
||||
if (value != null && value.Length != vertices.Length)
|
||||
throw new ArgumentException(string.Format("The vertex normals must be as many as the vertices. Assigned: {0} Require: {1}", value.Length, vertices.Length));
|
||||
|
||||
normals = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the tangents for this mesh.
|
||||
/// </summary>
|
||||
public Vector4[] Tangents
|
||||
{
|
||||
get { return tangents; }
|
||||
set
|
||||
{
|
||||
if (value != null && value.Length != vertices.Length)
|
||||
throw new ArgumentException(string.Format("The vertex tangents must be as many as the vertices. Assigned: {0} Require: {1}", value.Length, vertices.Length));
|
||||
|
||||
tangents = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the first UV set for this mesh.
|
||||
/// </summary>
|
||||
public Vector2[] UV1
|
||||
{
|
||||
get { return GetUVs2D(0); }
|
||||
set { SetUVs(0, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the second UV set for this mesh.
|
||||
/// </summary>
|
||||
public Vector2[] UV2
|
||||
{
|
||||
get { return GetUVs2D(1); }
|
||||
set { SetUVs(1, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the third UV set for this mesh.
|
||||
/// </summary>
|
||||
public Vector2[] UV3
|
||||
{
|
||||
get { return GetUVs2D(2); }
|
||||
set { SetUVs(2, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the fourth UV set for this mesh.
|
||||
/// </summary>
|
||||
public Vector2[] UV4
|
||||
{
|
||||
get { return GetUVs2D(3); }
|
||||
set { SetUVs(3, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the vertex colors for this mesh.
|
||||
/// </summary>
|
||||
public Vector4[] Colors
|
||||
{
|
||||
get { return colors; }
|
||||
set
|
||||
{
|
||||
if (value != null && value.Length != vertices.Length)
|
||||
throw new ArgumentException(string.Format("The vertex colors must be as many as the vertices. Assigned: {0} Require: {1}", value.Length, vertices.Length));
|
||||
|
||||
colors = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the vertex bone weights for this mesh.
|
||||
/// </summary>
|
||||
public BoneWeight[] BoneWeights
|
||||
{
|
||||
get { return boneWeights; }
|
||||
set
|
||||
{
|
||||
if (value != null && value.Length != vertices.Length)
|
||||
throw new ArgumentException(string.Format("The vertex bone weights must be as many as the vertices. Assigned: {0} Require: {1}", value.Length, vertices.Length));
|
||||
|
||||
boneWeights = value;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Creates a new mesh.
|
||||
/// </summary>
|
||||
/// <param name="vertices">The mesh vertices.</param>
|
||||
/// <param name="indices">The mesh indices.</param>
|
||||
public Mesh(Vector3d[] vertices, int[] indices)
|
||||
{
|
||||
if (vertices == null)
|
||||
throw new ArgumentNullException("vertices");
|
||||
else if (indices == null)
|
||||
throw new ArgumentNullException("indices");
|
||||
else if ((indices.Length % 3) != 0)
|
||||
throw new ArgumentException("The index count must be multiple by 3.", "indices");
|
||||
|
||||
this.vertices = vertices;
|
||||
this.indices = new int[1][];
|
||||
this.indices[0] = indices;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new mesh.
|
||||
/// </summary>
|
||||
/// <param name="vertices">The mesh vertices.</param>
|
||||
/// <param name="indices">The mesh indices.</param>
|
||||
public Mesh(Vector3d[] vertices, int[][] indices)
|
||||
{
|
||||
if (vertices == null)
|
||||
throw new ArgumentNullException("vertices");
|
||||
else if (indices == null)
|
||||
throw new ArgumentNullException("indices");
|
||||
|
||||
for (int i = 0; i < indices.Length; i++)
|
||||
{
|
||||
if (indices[i] != null && (indices[i].Length % 3) != 0)
|
||||
throw new ArgumentException(string.Format("The index count must be multiple by 3 at sub-mesh index {0}.", i), "indices");
|
||||
}
|
||||
|
||||
this.vertices = vertices;
|
||||
this.indices = indices;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
private void ClearVertexAttributes()
|
||||
{
|
||||
normals = null;
|
||||
tangents = null;
|
||||
uvs2D = null;
|
||||
uvs3D = null;
|
||||
uvs4D = null;
|
||||
colors = null;
|
||||
boneWeights = null;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
#region Recalculate Normals
|
||||
/// <summary>
|
||||
/// Recalculates the normals for this mesh smoothly.
|
||||
/// </summary>
|
||||
public void RecalculateNormals()
|
||||
{
|
||||
int vertexCount = vertices.Length;
|
||||
Vector3[] normals = new Vector3[vertexCount];
|
||||
|
||||
int subMeshCount = this.indices.Length;
|
||||
for (int subMeshIndex = 0; subMeshIndex < subMeshCount; subMeshIndex++)
|
||||
{
|
||||
int[] indices = this.indices[subMeshIndex];
|
||||
if (indices == null)
|
||||
continue;
|
||||
|
||||
int indexCount = indices.Length;
|
||||
for (int i = 0; i < indexCount; i += 3)
|
||||
{
|
||||
int i0 = indices[i];
|
||||
int i1 = indices[i + 1];
|
||||
int i2 = indices[i + 2];
|
||||
|
||||
var v0 = (Vector3)vertices[i0];
|
||||
var v1 = (Vector3)vertices[i1];
|
||||
var v2 = (Vector3)vertices[i2];
|
||||
|
||||
var nx = v1 - v0;
|
||||
var ny = v2 - v0;
|
||||
Vector3 normal;
|
||||
Vector3.Cross(ref nx, ref ny, out normal);
|
||||
normal.Normalize();
|
||||
|
||||
normals[i0] += normal;
|
||||
normals[i1] += normal;
|
||||
normals[i2] += normal;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < vertexCount; i++)
|
||||
{
|
||||
normals[i].Normalize();
|
||||
}
|
||||
|
||||
this.normals = normals;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Recalculate Tangents
|
||||
/// <summary>
|
||||
/// Recalculates the tangents for this mesh.
|
||||
/// </summary>
|
||||
public void RecalculateTangents()
|
||||
{
|
||||
// Make sure we have the normals first
|
||||
if (normals == null)
|
||||
return;
|
||||
|
||||
// Also make sure that we have the first UV set
|
||||
bool uvIs2D = (uvs2D != null && uvs2D[0] != null);
|
||||
bool uvIs3D = (uvs3D != null && uvs3D[0] != null);
|
||||
bool uvIs4D = (uvs4D != null && uvs4D[0] != null);
|
||||
if (!uvIs2D && !uvIs3D && !uvIs4D)
|
||||
return;
|
||||
|
||||
int vertexCount = vertices.Length;
|
||||
|
||||
var tangents = new Vector4[vertexCount];
|
||||
var tan1 = new Vector3[vertexCount];
|
||||
var tan2 = new Vector3[vertexCount];
|
||||
|
||||
Vector2[] uv2D = (uvIs2D ? uvs2D[0] : null);
|
||||
Vector3[] uv3D = (uvIs3D ? uvs3D[0] : null);
|
||||
Vector4[] uv4D = (uvIs4D ? uvs4D[0] : null);
|
||||
|
||||
int subMeshCount = this.indices.Length;
|
||||
for (int subMeshIndex = 0; subMeshIndex < subMeshCount; subMeshIndex++)
|
||||
{
|
||||
int[] indices = this.indices[subMeshIndex];
|
||||
if (indices == null)
|
||||
continue;
|
||||
|
||||
int indexCount = indices.Length;
|
||||
for (int i = 0; i < indexCount; i += 3)
|
||||
{
|
||||
int i0 = indices[i];
|
||||
int i1 = indices[i + 1];
|
||||
int i2 = indices[i + 2];
|
||||
|
||||
var v0 = vertices[i0];
|
||||
var v1 = vertices[i1];
|
||||
var v2 = vertices[i2];
|
||||
|
||||
float s1, s2, t1, t2;
|
||||
if (uvIs2D)
|
||||
{
|
||||
var w0 = uv2D[i0];
|
||||
var w1 = uv2D[i1];
|
||||
var w2 = uv2D[i2];
|
||||
s1 = w1.x - w0.x;
|
||||
s2 = w2.x - w0.x;
|
||||
t1 = w1.y - w0.y;
|
||||
t2 = w2.y - w0.y;
|
||||
}
|
||||
else if (uvIs3D)
|
||||
{
|
||||
var w0 = uv3D[i0];
|
||||
var w1 = uv3D[i1];
|
||||
var w2 = uv3D[i2];
|
||||
s1 = w1.x - w0.x;
|
||||
s2 = w2.x - w0.x;
|
||||
t1 = w1.y - w0.y;
|
||||
t2 = w2.y - w0.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
var w0 = uv4D[i0];
|
||||
var w1 = uv4D[i1];
|
||||
var w2 = uv4D[i2];
|
||||
s1 = w1.x - w0.x;
|
||||
s2 = w2.x - w0.x;
|
||||
t1 = w1.y - w0.y;
|
||||
t2 = w2.y - w0.y;
|
||||
}
|
||||
|
||||
|
||||
float x1 = (float)(v1.x - v0.x);
|
||||
float x2 = (float)(v2.x - v0.x);
|
||||
float y1 = (float)(v1.y - v0.y);
|
||||
float y2 = (float)(v2.y - v0.y);
|
||||
float z1 = (float)(v1.z - v0.z);
|
||||
float z2 = (float)(v2.z - v0.z);
|
||||
float r = 1f / (s1 * t2 - s2 * t1);
|
||||
|
||||
var sdir = new Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r);
|
||||
var tdir = new Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r);
|
||||
|
||||
tan1[i0] += sdir;
|
||||
tan1[i1] += sdir;
|
||||
tan1[i2] += sdir;
|
||||
tan2[i0] += tdir;
|
||||
tan2[i1] += tdir;
|
||||
tan2[i2] += tdir;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < vertexCount; i++)
|
||||
{
|
||||
var n = normals[i];
|
||||
var t = tan1[i];
|
||||
|
||||
var tmp = (t - n * Vector3.Dot(ref n, ref t));
|
||||
tmp.Normalize();
|
||||
|
||||
Vector3 c;
|
||||
Vector3.Cross(ref n, ref t, out c);
|
||||
float dot = Vector3.Dot(ref c, ref tan2[i]);
|
||||
float w = (dot < 0f ? -1f : 1f);
|
||||
tangents[i] = new Vector4(tmp.x, tmp.y, tmp.z, w);
|
||||
}
|
||||
|
||||
this.tangents = tangents;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Triangles
|
||||
/// <summary>
|
||||
/// Returns the count of triangles for a specific sub-mesh in this mesh.
|
||||
/// </summary>
|
||||
/// <param name="subMeshIndex">The sub-mesh index.</param>
|
||||
/// <returns>The triangle count.</returns>
|
||||
public int GetTriangleCount(int subMeshIndex)
|
||||
{
|
||||
if (subMeshIndex < 0 || subMeshIndex >= indices.Length)
|
||||
throw new IndexOutOfRangeException();
|
||||
|
||||
return indices[subMeshIndex].Length / 3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the triangle indices of a specific sub-mesh in this mesh.
|
||||
/// </summary>
|
||||
/// <param name="subMeshIndex">The sub-mesh index.</param>
|
||||
/// <returns>The triangle indices.</returns>
|
||||
public int[] GetIndices(int subMeshIndex)
|
||||
{
|
||||
if (subMeshIndex < 0 || subMeshIndex >= indices.Length)
|
||||
throw new IndexOutOfRangeException();
|
||||
|
||||
return indices[subMeshIndex] ?? emptyIndices;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the triangle indices for all sub-meshes in this mesh.
|
||||
/// </summary>
|
||||
/// <returns>The sub-mesh triangle indices.</returns>
|
||||
public int[][] GetSubMeshIndices()
|
||||
{
|
||||
var subMeshIndices = new int[indices.Length][];
|
||||
for (int subMeshIndex = 0; subMeshIndex < indices.Length; subMeshIndex++)
|
||||
{
|
||||
subMeshIndices[subMeshIndex] = indices[subMeshIndex] ?? emptyIndices;
|
||||
}
|
||||
return subMeshIndices;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the triangle indices of a specific sub-mesh in this mesh.
|
||||
/// </summary>
|
||||
/// <param name="subMeshIndex">The sub-mesh index.</param>
|
||||
/// <param name="indices">The triangle indices.</param>
|
||||
public void SetIndices(int subMeshIndex, int[] indices)
|
||||
{
|
||||
if (subMeshIndex < 0 || subMeshIndex >= this.indices.Length)
|
||||
throw new IndexOutOfRangeException();
|
||||
else if (indices == null)
|
||||
throw new ArgumentNullException("indices");
|
||||
else if ((indices.Length % 3) != 0)
|
||||
throw new ArgumentException("The index count must be multiple by 3.", "indices");
|
||||
|
||||
this.indices[subMeshIndex] = indices;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region UV Sets
|
||||
#region Getting
|
||||
/// <summary>
|
||||
/// Returns the UV dimension for a specific channel.
|
||||
/// </summary>
|
||||
/// <param name="channel"></param>
|
||||
/// <returns>The UV dimension count.</returns>
|
||||
public int GetUVDimension(int channel)
|
||||
{
|
||||
if (channel < 0 || channel >= UVChannelCount)
|
||||
throw new ArgumentOutOfRangeException("channel");
|
||||
|
||||
if (uvs2D != null && uvs2D[channel] != null)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
else if (uvs3D != null && uvs3D[channel] != null)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
else if (uvs4D != null && uvs4D[channel] != null)
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the UVs (2D) from a specific channel.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel index.</param>
|
||||
/// <returns>The UVs.</returns>
|
||||
public Vector2[] GetUVs2D(int channel)
|
||||
{
|
||||
if (channel < 0 || channel >= UVChannelCount)
|
||||
throw new ArgumentOutOfRangeException("channel");
|
||||
|
||||
if (uvs2D != null && uvs2D[channel] != null)
|
||||
{
|
||||
return uvs2D[channel];
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the UVs (3D) from a specific channel.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel index.</param>
|
||||
/// <returns>The UVs.</returns>
|
||||
public Vector3[] GetUVs3D(int channel)
|
||||
{
|
||||
if (channel < 0 || channel >= UVChannelCount)
|
||||
throw new ArgumentOutOfRangeException("channel");
|
||||
|
||||
if (uvs3D != null && uvs3D[channel] != null)
|
||||
{
|
||||
return uvs3D[channel];
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the UVs (4D) from a specific channel.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel index.</param>
|
||||
/// <returns>The UVs.</returns>
|
||||
public Vector4[] GetUVs4D(int channel)
|
||||
{
|
||||
if (channel < 0 || channel >= UVChannelCount)
|
||||
throw new ArgumentOutOfRangeException("channel");
|
||||
|
||||
if (uvs4D != null && uvs4D[channel] != null)
|
||||
{
|
||||
return uvs4D[channel];
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the UVs (2D) from a specific channel.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel index.</param>
|
||||
/// <param name="uvs">The UVs.</param>
|
||||
public void GetUVs(int channel, List<Vector2> uvs)
|
||||
{
|
||||
if (channel < 0 || channel >= UVChannelCount)
|
||||
throw new ArgumentOutOfRangeException("channel");
|
||||
else if (uvs == null)
|
||||
throw new ArgumentNullException("uvs");
|
||||
|
||||
uvs.Clear();
|
||||
if (uvs2D != null && uvs2D[channel] != null)
|
||||
{
|
||||
var uvData = uvs2D[channel];
|
||||
if (uvData != null)
|
||||
{
|
||||
uvs.AddRange(uvData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the UVs (3D) from a specific channel.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel index.</param>
|
||||
/// <param name="uvs">The UVs.</param>
|
||||
public void GetUVs(int channel, List<Vector3> uvs)
|
||||
{
|
||||
if (channel < 0 || channel >= UVChannelCount)
|
||||
throw new ArgumentOutOfRangeException("channel");
|
||||
else if (uvs == null)
|
||||
throw new ArgumentNullException("uvs");
|
||||
|
||||
uvs.Clear();
|
||||
if (uvs3D != null && uvs3D[channel] != null)
|
||||
{
|
||||
var uvData = uvs3D[channel];
|
||||
if (uvData != null)
|
||||
{
|
||||
uvs.AddRange(uvData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the UVs (4D) from a specific channel.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel index.</param>
|
||||
/// <param name="uvs">The UVs.</param>
|
||||
public void GetUVs(int channel, List<Vector4> uvs)
|
||||
{
|
||||
if (channel < 0 || channel >= UVChannelCount)
|
||||
throw new ArgumentOutOfRangeException("channel");
|
||||
else if (uvs == null)
|
||||
throw new ArgumentNullException("uvs");
|
||||
|
||||
uvs.Clear();
|
||||
if (uvs4D != null && uvs4D[channel] != null)
|
||||
{
|
||||
var uvData = uvs4D[channel];
|
||||
if (uvData != null)
|
||||
{
|
||||
uvs.AddRange(uvData);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Setting
|
||||
/// <summary>
|
||||
/// Sets the UVs (2D) for a specific channel.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel index.</param>
|
||||
/// <param name="uvs">The UVs.</param>
|
||||
public void SetUVs(int channel, Vector2[] uvs)
|
||||
{
|
||||
if (channel < 0 || channel >= UVChannelCount)
|
||||
throw new ArgumentOutOfRangeException("channel");
|
||||
|
||||
if (uvs != null && uvs.Length > 0)
|
||||
{
|
||||
if (uvs.Length != vertices.Length)
|
||||
throw new ArgumentException(string.Format("The vertex UVs must be as many as the vertices. Assigned: {0} Require: {1}", uvs.Length, vertices.Length));
|
||||
|
||||
if (uvs2D == null)
|
||||
uvs2D = new Vector2[UVChannelCount][];
|
||||
|
||||
int uvCount = uvs.Length;
|
||||
var uvSet = new Vector2[uvCount];
|
||||
uvs2D[channel] = uvSet;
|
||||
uvs.CopyTo(uvSet, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (uvs2D != null)
|
||||
{
|
||||
uvs2D[channel] = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (uvs3D != null)
|
||||
{
|
||||
uvs3D[channel] = null;
|
||||
}
|
||||
if (uvs4D != null)
|
||||
{
|
||||
uvs4D[channel] = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the UVs (3D) for a specific channel.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel index.</param>
|
||||
/// <param name="uvs">The UVs.</param>
|
||||
public void SetUVs(int channel, Vector3[] uvs)
|
||||
{
|
||||
if (channel < 0 || channel >= UVChannelCount)
|
||||
throw new ArgumentOutOfRangeException("channel");
|
||||
|
||||
if (uvs != null && uvs.Length > 0)
|
||||
{
|
||||
int uvCount = uvs.Length;
|
||||
if (uvCount != vertices.Length)
|
||||
throw new ArgumentException(string.Format("The vertex UVs must be as many as the vertices. Assigned: {0} Require: {1}", uvCount, vertices.Length), "uvs");
|
||||
|
||||
if (uvs3D == null)
|
||||
uvs3D = new Vector3[UVChannelCount][];
|
||||
|
||||
var uvSet = new Vector3[uvCount];
|
||||
uvs3D[channel] = uvSet;
|
||||
uvs.CopyTo(uvSet, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (uvs3D != null)
|
||||
{
|
||||
uvs3D[channel] = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (uvs2D != null)
|
||||
{
|
||||
uvs2D[channel] = null;
|
||||
}
|
||||
if (uvs4D != null)
|
||||
{
|
||||
uvs4D[channel] = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the UVs (4D) for a specific channel.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel index.</param>
|
||||
/// <param name="uvs">The UVs.</param>
|
||||
public void SetUVs(int channel, Vector4[] uvs)
|
||||
{
|
||||
if (channel < 0 || channel >= UVChannelCount)
|
||||
throw new ArgumentOutOfRangeException("channel");
|
||||
|
||||
if (uvs != null && uvs.Length > 0)
|
||||
{
|
||||
int uvCount = uvs.Length;
|
||||
if (uvCount != vertices.Length)
|
||||
throw new ArgumentException(string.Format("The vertex UVs must be as many as the vertices. Assigned: {0} Require: {1}", uvCount, vertices.Length), "uvs");
|
||||
|
||||
if (uvs4D == null)
|
||||
uvs4D = new Vector4[UVChannelCount][];
|
||||
|
||||
var uvSet = new Vector4[uvCount];
|
||||
uvs4D[channel] = uvSet;
|
||||
uvs.CopyTo(uvSet, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (uvs4D != null)
|
||||
{
|
||||
uvs4D[channel] = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (uvs2D != null)
|
||||
{
|
||||
uvs2D[channel] = null;
|
||||
}
|
||||
if (uvs3D != null)
|
||||
{
|
||||
uvs3D[channel] = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the UVs (2D) for a specific channel.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel index.</param>
|
||||
/// <param name="uvs">The UVs.</param>
|
||||
public void SetUVs(int channel, List<Vector2> uvs)
|
||||
{
|
||||
if (channel < 0 || channel >= UVChannelCount)
|
||||
throw new ArgumentOutOfRangeException("channel");
|
||||
|
||||
if (uvs != null && uvs.Count > 0)
|
||||
{
|
||||
int uvCount = uvs.Count;
|
||||
if (uvCount != vertices.Length)
|
||||
throw new ArgumentException(string.Format("The vertex UVs must be as many as the vertices. Assigned: {0} Require: {1}", uvCount, vertices.Length), "uvs");
|
||||
|
||||
if (uvs2D == null)
|
||||
uvs2D = new Vector2[UVChannelCount][];
|
||||
|
||||
var uvSet = new Vector2[uvCount];
|
||||
uvs2D[channel] = uvSet;
|
||||
uvs.CopyTo(uvSet, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (uvs2D != null)
|
||||
{
|
||||
uvs2D[channel] = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (uvs3D != null)
|
||||
{
|
||||
uvs3D[channel] = null;
|
||||
}
|
||||
if (uvs4D != null)
|
||||
{
|
||||
uvs4D[channel] = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the UVs (3D) for a specific channel.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel index.</param>
|
||||
/// <param name="uvs">The UVs.</param>
|
||||
public void SetUVs(int channel, List<Vector3> uvs)
|
||||
{
|
||||
if (channel < 0 || channel >= UVChannelCount)
|
||||
throw new ArgumentOutOfRangeException("channel");
|
||||
|
||||
if (uvs != null && uvs.Count > 0)
|
||||
{
|
||||
int uvCount = uvs.Count;
|
||||
if (uvCount != vertices.Length)
|
||||
throw new ArgumentException(string.Format("The vertex UVs must be as many as the vertices. Assigned: {0} Require: {1}", uvCount, vertices.Length), "uvs");
|
||||
|
||||
if (uvs3D == null)
|
||||
uvs3D = new Vector3[UVChannelCount][];
|
||||
|
||||
var uvSet = new Vector3[uvCount];
|
||||
uvs3D[channel] = uvSet;
|
||||
uvs.CopyTo(uvSet, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (uvs3D != null)
|
||||
{
|
||||
uvs3D[channel] = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (uvs2D != null)
|
||||
{
|
||||
uvs2D[channel] = null;
|
||||
}
|
||||
if (uvs4D != null)
|
||||
{
|
||||
uvs4D[channel] = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the UVs (4D) for a specific channel.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel index.</param>
|
||||
/// <param name="uvs">The UVs.</param>
|
||||
public void SetUVs(int channel, List<Vector4> uvs)
|
||||
{
|
||||
if (channel < 0 || channel >= UVChannelCount)
|
||||
throw new ArgumentOutOfRangeException("channel");
|
||||
|
||||
if (uvs != null && uvs.Count > 0)
|
||||
{
|
||||
int uvCount = uvs.Count;
|
||||
if (uvCount != vertices.Length)
|
||||
throw new ArgumentException(string.Format("The vertex UVs must be as many as the vertices. Assigned: {0} Require: {1}", uvCount, vertices.Length), "uvs");
|
||||
|
||||
if (uvs4D == null)
|
||||
uvs4D = new Vector4[UVChannelCount][];
|
||||
|
||||
var uvSet = new Vector4[uvCount];
|
||||
uvs4D[channel] = uvSet;
|
||||
uvs.CopyTo(uvSet, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (uvs4D != null)
|
||||
{
|
||||
uvs4D[channel] = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (uvs2D != null)
|
||||
{
|
||||
uvs2D[channel] = null;
|
||||
}
|
||||
if (uvs3D != null)
|
||||
{
|
||||
uvs3D[channel] = null;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region To String
|
||||
/// <summary>
|
||||
/// Returns the text-representation of this mesh.
|
||||
/// </summary>
|
||||
/// <returns>The text-representation.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("Vertices: {0}", vertices.Length);
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
180
LightlessSync/ThirdParty/MeshDecimator/MeshDecimation.cs
vendored
Normal file
180
LightlessSync/ThirdParty/MeshDecimator/MeshDecimation.cs
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
#region License
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright(c) 2017-2018 Mattias Edlund
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using MeshDecimator.Algorithms;
|
||||
|
||||
namespace MeshDecimator
|
||||
{
|
||||
#region Algorithm
|
||||
/// <summary>
|
||||
/// The decimation algorithms.
|
||||
/// </summary>
|
||||
public enum Algorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// The default algorithm.
|
||||
/// </summary>
|
||||
Default,
|
||||
/// <summary>
|
||||
/// The fast quadric mesh simplification algorithm.
|
||||
/// </summary>
|
||||
FastQuadricMesh
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// The mesh decimation API.
|
||||
/// </summary>
|
||||
public static class MeshDecimation
|
||||
{
|
||||
#region Public Methods
|
||||
#region Create Algorithm
|
||||
/// <summary>
|
||||
/// Creates a specific decimation algorithm.
|
||||
/// </summary>
|
||||
/// <param name="algorithm">The desired algorithm.</param>
|
||||
/// <returns>The decimation algorithm.</returns>
|
||||
public static DecimationAlgorithm CreateAlgorithm(Algorithm algorithm)
|
||||
{
|
||||
DecimationAlgorithm alg = null;
|
||||
|
||||
switch (algorithm)
|
||||
{
|
||||
case Algorithm.Default:
|
||||
case Algorithm.FastQuadricMesh:
|
||||
alg = new FastQuadricMeshSimplification();
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("The specified algorithm is not supported.", "algorithm");
|
||||
}
|
||||
|
||||
return alg;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Decimate Mesh
|
||||
/// <summary>
|
||||
/// Decimates a mesh.
|
||||
/// </summary>
|
||||
/// <param name="mesh">The mesh to decimate.</param>
|
||||
/// <param name="targetTriangleCount">The target triangle count.</param>
|
||||
/// <returns>The decimated mesh.</returns>
|
||||
public static Mesh DecimateMesh(Mesh mesh, int targetTriangleCount)
|
||||
{
|
||||
return DecimateMesh(Algorithm.Default, mesh, targetTriangleCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decimates a mesh.
|
||||
/// </summary>
|
||||
/// <param name="algorithm">The desired algorithm.</param>
|
||||
/// <param name="mesh">The mesh to decimate.</param>
|
||||
/// <param name="targetTriangleCount">The target triangle count.</param>
|
||||
/// <returns>The decimated mesh.</returns>
|
||||
public static Mesh DecimateMesh(Algorithm algorithm, Mesh mesh, int targetTriangleCount)
|
||||
{
|
||||
if (mesh == null)
|
||||
throw new ArgumentNullException("mesh");
|
||||
|
||||
var decimationAlgorithm = CreateAlgorithm(algorithm);
|
||||
return DecimateMesh(decimationAlgorithm, mesh, targetTriangleCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decimates a mesh.
|
||||
/// </summary>
|
||||
/// <param name="algorithm">The decimation algorithm.</param>
|
||||
/// <param name="mesh">The mesh to decimate.</param>
|
||||
/// <param name="targetTriangleCount">The target triangle count.</param>
|
||||
/// <returns>The decimated mesh.</returns>
|
||||
public static Mesh DecimateMesh(DecimationAlgorithm algorithm, Mesh mesh, int targetTriangleCount)
|
||||
{
|
||||
if (algorithm == null)
|
||||
throw new ArgumentNullException("algorithm");
|
||||
else if (mesh == null)
|
||||
throw new ArgumentNullException("mesh");
|
||||
|
||||
int currentTriangleCount = mesh.TriangleCount;
|
||||
if (targetTriangleCount > currentTriangleCount)
|
||||
targetTriangleCount = currentTriangleCount;
|
||||
else if (targetTriangleCount < 0)
|
||||
targetTriangleCount = 0;
|
||||
|
||||
algorithm.Initialize(mesh);
|
||||
algorithm.DecimateMesh(targetTriangleCount);
|
||||
return algorithm.ToMesh();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Decimate Mesh Lossless
|
||||
/// <summary>
|
||||
/// Decimates a mesh without losing any quality.
|
||||
/// </summary>
|
||||
/// <param name="mesh">The mesh to decimate.</param>
|
||||
/// <returns>The decimated mesh.</returns>
|
||||
public static Mesh DecimateMeshLossless(Mesh mesh)
|
||||
{
|
||||
return DecimateMeshLossless(Algorithm.Default, mesh);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decimates a mesh without losing any quality.
|
||||
/// </summary>
|
||||
/// <param name="algorithm">The desired algorithm.</param>
|
||||
/// <param name="mesh">The mesh to decimate.</param>
|
||||
/// <returns>The decimated mesh.</returns>
|
||||
public static Mesh DecimateMeshLossless(Algorithm algorithm, Mesh mesh)
|
||||
{
|
||||
if (mesh == null)
|
||||
throw new ArgumentNullException("mesh");
|
||||
|
||||
var decimationAlgorithm = CreateAlgorithm(algorithm);
|
||||
return DecimateMeshLossless(decimationAlgorithm, mesh);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decimates a mesh without losing any quality.
|
||||
/// </summary>
|
||||
/// <param name="algorithm">The decimation algorithm.</param>
|
||||
/// <param name="mesh">The mesh to decimate.</param>
|
||||
/// <returns>The decimated mesh.</returns>
|
||||
public static Mesh DecimateMeshLossless(DecimationAlgorithm algorithm, Mesh mesh)
|
||||
{
|
||||
if (algorithm == null)
|
||||
throw new ArgumentNullException("algorithm");
|
||||
else if (mesh == null)
|
||||
throw new ArgumentNullException("mesh");
|
||||
|
||||
int currentTriangleCount = mesh.TriangleCount;
|
||||
algorithm.Initialize(mesh);
|
||||
algorithm.DecimateMeshLossless();
|
||||
return algorithm.ToMesh();
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -944,6 +944,7 @@ public class CompactUi : WindowMediatorSubscriberBase
|
||||
VisiblePairSortMode.VramUsage => SortVisibleByMetric(entryList, e => e.LastAppliedApproximateVramBytes),
|
||||
VisiblePairSortMode.EffectiveVramUsage => SortVisibleByMetric(entryList, e => e.LastAppliedApproximateEffectiveVramBytes),
|
||||
VisiblePairSortMode.TriangleCount => SortVisibleByMetric(entryList, e => e.LastAppliedDataTris),
|
||||
VisiblePairSortMode.EffectiveTriangleCount => SortVisibleByMetric(entryList, e => e.LastAppliedApproximateEffectiveTris),
|
||||
VisiblePairSortMode.Alphabetical => [.. entryList.OrderBy(e => AlphabeticalSortKey(e), StringComparer.OrdinalIgnoreCase)],
|
||||
VisiblePairSortMode.PreferredDirectPairs => SortVisibleByPreferred(entryList),
|
||||
_ => SortEntries(entryList),
|
||||
|
||||
@@ -326,6 +326,7 @@ public class DrawFolderTag : DrawFolderBase
|
||||
VisiblePairSortMode.VramUsage => "VRAM usage (descending)",
|
||||
VisiblePairSortMode.EffectiveVramUsage => "Effective VRAM usage (descending)",
|
||||
VisiblePairSortMode.TriangleCount => "Triangle count (descending)",
|
||||
VisiblePairSortMode.EffectiveTriangleCount => "Effective triangle count (descending)",
|
||||
VisiblePairSortMode.PreferredDirectPairs => "Preferred permissions & Direct pairs",
|
||||
_ => "Default",
|
||||
};
|
||||
|
||||
@@ -429,6 +429,7 @@ public class DrawUserPair
|
||||
_pair.LastAppliedApproximateVRAMBytes,
|
||||
_pair.LastAppliedApproximateEffectiveVRAMBytes,
|
||||
_pair.LastAppliedDataTris,
|
||||
_pair.LastAppliedApproximateEffectiveTris,
|
||||
_pair.IsPaired,
|
||||
groupDisplays is null ? ImmutableArray<string>.Empty : ImmutableArray.CreateRange(groupDisplays));
|
||||
|
||||
@@ -444,6 +445,8 @@ public class DrawUserPair
|
||||
private static string BuildTooltip(in TooltipSnapshot snapshot)
|
||||
{
|
||||
var builder = new StringBuilder(256);
|
||||
static string FormatTriangles(long count) =>
|
||||
count > 1000 ? (count / 1000d).ToString("0.0'k'") : count.ToString();
|
||||
|
||||
if (snapshot.IsPaused)
|
||||
{
|
||||
@@ -510,9 +513,13 @@ public class DrawUserPair
|
||||
{
|
||||
builder.Append(Environment.NewLine);
|
||||
builder.Append("Approx. Triangle Count (excl. Vanilla): ");
|
||||
builder.Append(snapshot.LastAppliedDataTris > 1000
|
||||
? (snapshot.LastAppliedDataTris / 1000d).ToString("0.0'k'")
|
||||
: snapshot.LastAppliedDataTris);
|
||||
builder.Append(FormatTriangles(snapshot.LastAppliedDataTris));
|
||||
if (snapshot.LastAppliedApproximateEffectiveTris >= 0)
|
||||
{
|
||||
builder.Append(" (Effective: ");
|
||||
builder.Append(FormatTriangles(snapshot.LastAppliedApproximateEffectiveTris));
|
||||
builder.Append(')');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -544,11 +551,12 @@ public class DrawUserPair
|
||||
long LastAppliedApproximateVRAMBytes,
|
||||
long LastAppliedApproximateEffectiveVRAMBytes,
|
||||
long LastAppliedDataTris,
|
||||
long LastAppliedApproximateEffectiveTris,
|
||||
bool IsPaired,
|
||||
ImmutableArray<string> GroupDisplays)
|
||||
{
|
||||
public static TooltipSnapshot Empty { get; } =
|
||||
new(false, false, false, IndividualPairStatus.None, string.Empty, string.Empty, -1, -1, -1, -1, false, ImmutableArray<string>.Empty);
|
||||
new(false, false, false, IndividualPairStatus.None, string.Empty, string.Empty, -1, -1, -1, -1, -1, false, ImmutableArray<string>.Empty);
|
||||
}
|
||||
|
||||
private void DrawPairedClientMenu()
|
||||
|
||||
@@ -217,6 +217,7 @@ public class DrawEntityFactory
|
||||
entry.PairStatus,
|
||||
handler?.LastAppliedDataBytes ?? -1,
|
||||
handler?.LastAppliedDataTris ?? -1,
|
||||
handler?.LastAppliedApproximateEffectiveTris ?? -1,
|
||||
handler?.LastAppliedApproximateVRAMBytes ?? -1,
|
||||
handler?.LastAppliedApproximateEffectiveVRAMBytes ?? -1,
|
||||
handler);
|
||||
|
||||
@@ -415,7 +415,9 @@ public class IdDisplayHandler
|
||||
var vramBytes = pair.LastAppliedApproximateEffectiveVRAMBytes >= 0
|
||||
? pair.LastAppliedApproximateEffectiveVRAMBytes
|
||||
: pair.LastAppliedApproximateVRAMBytes;
|
||||
var triangleCount = pair.LastAppliedDataTris;
|
||||
var triangleCount = pair.LastAppliedApproximateEffectiveTris >= 0
|
||||
? pair.LastAppliedApproximateEffectiveTris
|
||||
: pair.LastAppliedDataTris;
|
||||
if (vramBytes < 0 && triangleCount < 0)
|
||||
{
|
||||
return null;
|
||||
|
||||
@@ -21,6 +21,7 @@ public sealed record PairUiEntry(
|
||||
IndividualPairStatus? PairStatus,
|
||||
long LastAppliedDataBytes,
|
||||
long LastAppliedDataTris,
|
||||
long LastAppliedApproximateEffectiveTris,
|
||||
long LastAppliedApproximateVramBytes,
|
||||
long LastAppliedApproximateEffectiveVramBytes,
|
||||
IPairHandlerAdapter? Handler)
|
||||
|
||||
@@ -7,4 +7,5 @@ public enum VisiblePairSortMode
|
||||
EffectiveVramUsage = 2,
|
||||
TriangleCount = 3,
|
||||
PreferredDirectPairs = 4,
|
||||
EffectiveTriangleCount = 5,
|
||||
}
|
||||
|
||||
@@ -574,6 +574,94 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawTriangleDecimationCounters()
|
||||
{
|
||||
HashSet<Pair> trackedPairs = new();
|
||||
|
||||
var snapshot = _pairUiService.GetSnapshot();
|
||||
|
||||
foreach (var pair in snapshot.DirectPairs)
|
||||
{
|
||||
trackedPairs.Add(pair);
|
||||
}
|
||||
|
||||
foreach (var group in snapshot.GroupPairs.Values)
|
||||
{
|
||||
foreach (var pair in group)
|
||||
{
|
||||
trackedPairs.Add(pair);
|
||||
}
|
||||
}
|
||||
|
||||
long totalOriginalTris = 0;
|
||||
long totalEffectiveTris = 0;
|
||||
var hasData = false;
|
||||
|
||||
foreach (var pair in trackedPairs)
|
||||
{
|
||||
if (!pair.IsVisible)
|
||||
continue;
|
||||
|
||||
var original = pair.LastAppliedDataTris;
|
||||
var effective = pair.LastAppliedApproximateEffectiveTris;
|
||||
|
||||
if (original >= 0)
|
||||
{
|
||||
hasData = true;
|
||||
totalOriginalTris += original;
|
||||
}
|
||||
|
||||
if (effective >= 0)
|
||||
{
|
||||
hasData = true;
|
||||
totalEffectiveTris += effective;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasData)
|
||||
{
|
||||
ImGui.TextDisabled("Triangle usage has not been calculated yet.");
|
||||
return;
|
||||
}
|
||||
|
||||
var savedTris = Math.Max(0L, totalOriginalTris - totalEffectiveTris);
|
||||
var originalText = FormatTriangleCount(totalOriginalTris);
|
||||
var effectiveText = FormatTriangleCount(totalEffectiveTris);
|
||||
var savedText = FormatTriangleCount(savedTris);
|
||||
|
||||
ImGui.TextUnformatted($"Total triangle usage (original): {originalText}");
|
||||
ImGui.TextUnformatted($"Total triangle usage (effective): {effectiveText}");
|
||||
|
||||
if (savedTris > 0)
|
||||
{
|
||||
UiSharedService.ColorText($"Triangles saved by decimation: {savedText}", UIColors.Get("LightlessGreen"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.TextUnformatted($"Triangles saved by decimation: {savedText}");
|
||||
}
|
||||
|
||||
static string FormatTriangleCount(long triangleCount)
|
||||
{
|
||||
if (triangleCount < 0)
|
||||
{
|
||||
return "n/a";
|
||||
}
|
||||
|
||||
if (triangleCount >= 1_000_000)
|
||||
{
|
||||
return FormattableString.Invariant($"{triangleCount / 1_000_000d:0.#}m tris");
|
||||
}
|
||||
|
||||
if (triangleCount >= 1_000)
|
||||
{
|
||||
return FormattableString.Invariant($"{triangleCount / 1_000d:0.#}k tris");
|
||||
}
|
||||
|
||||
return $"{triangleCount} tris";
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawThemeVectorRow(MainStyle.StyleVector2Option option)
|
||||
{
|
||||
ImGui.TableNextRow();
|
||||
@@ -1495,6 +1583,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
DrawPairPropertyRow("Approx. VRAM", FormatBytes(pair.LastAppliedApproximateVRAMBytes));
|
||||
DrawPairPropertyRow("Effective VRAM", FormatBytes(pair.LastAppliedApproximateEffectiveVRAMBytes));
|
||||
DrawPairPropertyRow("Last Triangles", pair.LastAppliedDataTris < 0 ? "n/a" : pair.LastAppliedDataTris.ToString(CultureInfo.InvariantCulture));
|
||||
DrawPairPropertyRow("Effective Triangles", pair.LastAppliedApproximateEffectiveTris < 0 ? "n/a" : pair.LastAppliedApproximateEffectiveTris.ToString(CultureInfo.InvariantCulture));
|
||||
ImGui.EndTable();
|
||||
}
|
||||
|
||||
@@ -3528,6 +3617,102 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
ImGui.TreePop();
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
if (_uiShared.MediumTreeNode("Model Optimization", UIColors.Get("DimRed")))
|
||||
{
|
||||
_uiShared.MediumText("Warning", UIColors.Get("DimRed"));
|
||||
_uiShared.DrawNoteLine("! ", UIColors.Get("DimRed"),
|
||||
new SeStringUtils.RichTextEntry("Model decimation is a "),
|
||||
new SeStringUtils.RichTextEntry("destructive", UIColors.Get("DimRed"), true),
|
||||
new SeStringUtils.RichTextEntry(" process and may cause broken or incorrect character appearances."));
|
||||
|
||||
_uiShared.DrawNoteLine("! ", UIColors.Get("DimRed"),
|
||||
new SeStringUtils.RichTextEntry("this shit is placeholder still owo"));
|
||||
|
||||
var performanceConfig = _playerPerformanceConfigService.Current;
|
||||
var enableDecimation = performanceConfig.EnableModelDecimation;
|
||||
if (ImGui.Checkbox("Enable model decimation", ref enableDecimation))
|
||||
{
|
||||
performanceConfig.EnableModelDecimation = enableDecimation;
|
||||
_playerPerformanceConfigService.Save();
|
||||
}
|
||||
_uiShared.DrawHelpText("When enabled, Lightless generates a decimated copy of given model after download.");
|
||||
|
||||
var triangleThreshold = performanceConfig.ModelDecimationTriangleThreshold;
|
||||
ImGui.SetNextItemWidth(300 * ImGuiHelpers.GlobalScale);
|
||||
if (ImGui.SliderInt("Decimate models above", ref triangleThreshold, 10_000, 100_000))
|
||||
{
|
||||
performanceConfig.ModelDecimationTriangleThreshold = Math.Clamp(triangleThreshold, 10_000, 100_000);
|
||||
_playerPerformanceConfigService.Save();
|
||||
}
|
||||
ImGui.SameLine();
|
||||
ImGui.Text("triangles");
|
||||
_uiShared.DrawHelpText($"Models below this triangle count are left untouched.{UiSharedService.TooltipSeparator}Default: 50,000");
|
||||
|
||||
var targetPercent = (float)(performanceConfig.ModelDecimationTargetRatio * 100.0);
|
||||
var clampedPercent = Math.Clamp(targetPercent, 70f, 99f);
|
||||
if (Math.Abs(clampedPercent - targetPercent) > float.Epsilon)
|
||||
{
|
||||
performanceConfig.ModelDecimationTargetRatio = clampedPercent / 100.0;
|
||||
_playerPerformanceConfigService.Save();
|
||||
targetPercent = clampedPercent;
|
||||
}
|
||||
ImGui.SetNextItemWidth(300 * ImGuiHelpers.GlobalScale);
|
||||
if (ImGui.SliderFloat("Target triangle ratio", ref targetPercent, 70f, 99f, "%.0f%%"))
|
||||
{
|
||||
performanceConfig.ModelDecimationTargetRatio = Math.Clamp(targetPercent / 100f, 0.7f, 0.99f);
|
||||
_playerPerformanceConfigService.Save();
|
||||
}
|
||||
_uiShared.DrawHelpText($"Target ratio relative to original triangle count (70% keeps 70% of triangles).{UiSharedService.TooltipSeparator}Default: 70%");
|
||||
|
||||
ImGui.Dummy(new Vector2(5));
|
||||
ImGui.TextUnformatted("Decimation targets");
|
||||
_uiShared.DrawHelpText("Hair mods are always excluded from decimation.");
|
||||
|
||||
var allowBody = performanceConfig.ModelDecimationAllowBody;
|
||||
if (ImGui.Checkbox("Body", ref allowBody))
|
||||
{
|
||||
performanceConfig.ModelDecimationAllowBody = allowBody;
|
||||
_playerPerformanceConfigService.Save();
|
||||
}
|
||||
|
||||
var allowFaceHead = performanceConfig.ModelDecimationAllowFaceHead;
|
||||
if (ImGui.Checkbox("Face/head", ref allowFaceHead))
|
||||
{
|
||||
performanceConfig.ModelDecimationAllowFaceHead = allowFaceHead;
|
||||
_playerPerformanceConfigService.Save();
|
||||
}
|
||||
|
||||
var allowTail = performanceConfig.ModelDecimationAllowTail;
|
||||
if (ImGui.Checkbox("Tails/Ears", ref allowTail))
|
||||
{
|
||||
performanceConfig.ModelDecimationAllowTail = allowTail;
|
||||
_playerPerformanceConfigService.Save();
|
||||
}
|
||||
|
||||
var allowClothing = performanceConfig.ModelDecimationAllowClothing;
|
||||
if (ImGui.Checkbox("Clothing (body/legs/shoes/gloves/hats)", ref allowClothing))
|
||||
{
|
||||
performanceConfig.ModelDecimationAllowClothing = allowClothing;
|
||||
_playerPerformanceConfigService.Save();
|
||||
}
|
||||
|
||||
var allowAccessories = performanceConfig.ModelDecimationAllowAccessories;
|
||||
if (ImGui.Checkbox("Accessories (earring/rings/bracelet/necklace)", ref allowAccessories))
|
||||
{
|
||||
performanceConfig.ModelDecimationAllowAccessories = allowAccessories;
|
||||
_playerPerformanceConfigService.Save();
|
||||
}
|
||||
|
||||
ImGui.Dummy(new Vector2(5));
|
||||
DrawTriangleDecimationCounters();
|
||||
ImGui.Dummy(new Vector2(5));
|
||||
|
||||
UiSharedService.ColoredSeparator(UIColors.Get("DimRed"), 1.5f);
|
||||
ImGui.TreePop();
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
ImGui.Dummy(new Vector2(10));
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using LightlessSync.FileCache;
|
||||
using LightlessSync.LightlessConfiguration;
|
||||
using LightlessSync.PlayerData.Handlers;
|
||||
using LightlessSync.Services.Mediator;
|
||||
using LightlessSync.Services.ModelDecimation;
|
||||
using LightlessSync.Services.TextureCompression;
|
||||
using LightlessSync.WebAPI.Files.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -25,6 +26,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
||||
private readonly FileTransferOrchestrator _orchestrator;
|
||||
private readonly LightlessConfigService _configService;
|
||||
private readonly TextureDownscaleService _textureDownscaleService;
|
||||
private readonly ModelDecimationService _modelDecimationService;
|
||||
private readonly TextureMetadataHelper _textureMetadataHelper;
|
||||
|
||||
private readonly ConcurrentDictionary<ThrottledStream, byte> _activeDownloadStreams;
|
||||
@@ -41,6 +43,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
||||
FileCompactor fileCompactor,
|
||||
LightlessConfigService configService,
|
||||
TextureDownscaleService textureDownscaleService,
|
||||
ModelDecimationService modelDecimationService,
|
||||
TextureMetadataHelper textureMetadataHelper) : base(logger, mediator)
|
||||
{
|
||||
_downloadStatus = new Dictionary<string, FileDownloadStatus>(StringComparer.Ordinal);
|
||||
@@ -49,6 +52,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
||||
_fileCompactor = fileCompactor;
|
||||
_configService = configService;
|
||||
_textureDownscaleService = textureDownscaleService;
|
||||
_modelDecimationService = modelDecimationService;
|
||||
_textureMetadataHelper = textureMetadataHelper;
|
||||
_activeDownloadStreams = new();
|
||||
_lastConfigDirectDownloadsState = _configService.Current.EnableDirectDownloads;
|
||||
@@ -1026,6 +1030,11 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
||||
() => _textureMetadataHelper.DetermineMapKind(gamePath, filePath));
|
||||
}
|
||||
|
||||
if (!skipDownscale && _modelDecimationService.ShouldScheduleDecimation(fileHash, filePath, gamePath))
|
||||
{
|
||||
_modelDecimationService.ScheduleDecimation(fileHash, filePath, gamePath);
|
||||
}
|
||||
|
||||
if (entry != null && !string.Equals(entry.Hash, fileHash, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Logger.LogError("Hash mismatch after extracting, got {hash}, expected {expectedHash}, deleting file",
|
||||
|
||||
Reference in New Issue
Block a user