diff --git a/Assets/Scripts/Engine/DataLoader/DxtTextureLoader.cs b/Assets/Scripts/Engine/DataLoader/DxtTextureLoader.cs index 795eb59f1..97060d2fe 100644 --- a/Assets/Scripts/Engine/DataLoader/DxtTextureLoader.cs +++ b/Assets/Scripts/Engine/DataLoader/DxtTextureLoader.cs @@ -67,7 +67,7 @@ public void Load(byte[] data, out bool hasAlphaChannel) private void LoadDxt1Texture(byte[] data) { _rawRgbaData = ArrayPool.Shared.Rent(_width * _height * 4); - Dxt1Decoder.ToRgba32NonAlloc(data, _width, _height, _rawRgbaData); + Dxt1Decoder.ToRgba32(data, _width, _height, _rawRgbaData); } // Texture2D.LoadRawTextureData does not support DXT3 format @@ -76,7 +76,7 @@ private void LoadDxt1Texture(byte[] data) private void LoadDxt3Texture(byte[] data) { _rawRgbaData = ArrayPool.Shared.Rent(_width * _height * 4); - Dxt3Decoder.ToRgba32NonAlloc(data, _width, _height, _rawRgbaData); + Dxt3Decoder.ToRgba32(data, _width, _height, _rawRgbaData); } public ITexture2D ToTexture() diff --git a/Assets/Scripts/Engine/DataLoader/TgaTextureLoader.cs b/Assets/Scripts/Engine/DataLoader/TgaTextureLoader.cs index 741844c3b..50e627009 100644 --- a/Assets/Scripts/Engine/DataLoader/TgaTextureLoader.cs +++ b/Assets/Scripts/Engine/DataLoader/TgaTextureLoader.cs @@ -8,6 +8,7 @@ namespace Engine.DataLoader using System; using System.Buffers; using Core.Abstraction; + using Pal3.Core.DataReader.Tga; /// /// .tga file loader and Texture2D converter. @@ -38,60 +39,24 @@ public unsafe void Load(byte[] data, out bool hasAlphaChannel) bitDepth = *(p + 4); } - var dataStartIndex = 18; - _rawRgbaData = ArrayPool.Shared.Rent(_width * _height * 4); switch (bitDepth) { case 24: hasAlphaChannel = false; - Decode24BitDataToRgba32(data, dataStartIndex); + TgaDecoder.Decode24BitDataToRgba32( + data, _width, _height, _rawRgbaData); break; case 32: - Decode32BitDataToRgba32(data, dataStartIndex, out hasAlphaChannel); + TgaDecoder.Decode32BitDataToRgba32( + data, _width, _height, _rawRgbaData, out hasAlphaChannel); break; default: throw new Exception("TGA texture had non 32/24 bit depth"); } } - private unsafe void Decode24BitDataToRgba32(byte[] data, int startIndex) - { - fixed (byte* srcStart = &data[startIndex], dstStart = _rawRgbaData) - { - byte* src = srcStart, dst = dstStart; - for (var i = 0; i < _width * _height; i++, src += 3, dst += 4) - { - *dst = *(src + 2); - *(dst + 1) = *(src + 1); - *(dst + 2) = *src; - *(dst + 3) = 0; // 24-bit don't have alpha - } - } - } - - private unsafe void Decode32BitDataToRgba32(byte[] data, int startIndex, out bool hasAlphaChannel) - { - hasAlphaChannel = false; - fixed (byte* srcStart = &data[startIndex], dstStart = _rawRgbaData) - { - var firstAlpha = *(srcStart + 3); - byte* src = srcStart, dst = dstStart; - for (var i = 0; i < _width * _height; i++, src += 4, dst += 4) - { - *dst = *(src + 2); - *(dst + 1) = *(src + 1); - *(dst + 2) = *src; - - byte alpha = *(src + 3); - *(dst + 3) = alpha; - - if (alpha != firstAlpha) hasAlphaChannel = true; - } - } - } - public ITexture2D ToTexture() { if (_rawRgbaData == null) return null; diff --git a/Assets/Scripts/Engine/Extensions/UnityPrimitivesConvertor.cs b/Assets/Scripts/Engine/Extensions/UnityPrimitivesConvertor.cs index 33662e220..55eaaa584 100644 --- a/Assets/Scripts/Engine/Extensions/UnityPrimitivesConvertor.cs +++ b/Assets/Scripts/Engine/Extensions/UnityPrimitivesConvertor.cs @@ -41,17 +41,17 @@ public static UnityEngine.Vector3[] ToUnityPositions(this GameBoxVector3[] gameB } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ToUnityPositionsNonAlloc(this GameBoxVector3[] gameBoxPositions, - UnityEngine.Vector3[] unityPositions, float scale = GameBoxUnitToUnityUnit) + public static void ToUnityPositions(this GameBoxVector3[] gameBoxPositions, + UnityEngine.Vector3[] unityPositionsBuffer, float scale = GameBoxUnitToUnityUnit) { - if (gameBoxPositions == null || unityPositions == null || unityPositions.Length != gameBoxPositions.Length) + if (gameBoxPositions == null || unityPositionsBuffer == null || unityPositionsBuffer.Length != gameBoxPositions.Length) { - throw new ArgumentException("unityPositions and gameBoxPositions must be non-null and have the same length"); + throw new ArgumentException("gameBoxPositions and unityPositionsBuffer must be non-null and have the same length"); } for (var i = 0; i < gameBoxPositions.Length; i++) { - unityPositions[i] = ToUnityVector3(gameBoxPositions[i], scale); + unityPositionsBuffer[i] = ToUnityVector3(gameBoxPositions[i], scale); } } @@ -158,17 +158,17 @@ public static UnityEngine.Vector3[] ToUnityNormals(this GameBoxVector3[] gameBox } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ToUnityNormalsNonAlloc(this GameBoxVector3[] gameBoxNormals, - UnityEngine.Vector3[] unityNormals) + public static void ToUnityNormals(this GameBoxVector3[] gameBoxNormals, + UnityEngine.Vector3[] unityNormalsBuffer) { - if (gameBoxNormals == null || unityNormals == null || gameBoxNormals.Length != unityNormals.Length) + if (gameBoxNormals == null || unityNormalsBuffer == null || gameBoxNormals.Length != unityNormalsBuffer.Length) { - throw new ArgumentException("gameBoxNormals and unityNormals must be non-null and have the same length"); + throw new ArgumentException("gameBoxNormals and unityNormalsBuffer must be non-null and have the same length"); } for (var i = 0; i < gameBoxNormals.Length; i++) { - unityNormals[i] = ToUnityNormal(gameBoxNormals[i]); + unityNormalsBuffer[i] = ToUnityNormal(gameBoxNormals[i]); } } @@ -264,16 +264,16 @@ public static void ToUnityTrianglesInPlace(this int[] gameBoxTriangles) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ToUnityTrianglesNonAlloc(this int[] gameBoxTriangles, int[] unityTriangles) + public static void ToUnityTriangles(this int[] gameBoxTriangles, int[] unityTrianglesBuffer) { - if (gameBoxTriangles == null || unityTriangles == null || gameBoxTriangles.Length != unityTriangles.Length) + if (gameBoxTriangles == null || unityTrianglesBuffer == null || gameBoxTriangles.Length != unityTrianglesBuffer.Length) { - throw new ArgumentException("gameBoxTriangles and unityTriangles must be non-null and have the same length"); + throw new ArgumentException("gameBoxTriangles and unityTrianglesBuffer must be non-null and have the same length"); } for (var i = 0; i < gameBoxTriangles.Length; i++) { - unityTriangles[i] = gameBoxTriangles[gameBoxTriangles.Length - 1 - i]; + unityTrianglesBuffer[i] = gameBoxTriangles[gameBoxTriangles.Length - 1 - i]; } } @@ -319,16 +319,16 @@ public static UnityEngine.Vector2[] ToUnityVector2s(this GameBoxVector2[] gameBo } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ToUnityVector2sNonAlloc(this GameBoxVector2[] gameBoxVector2s, UnityEngine.Vector2[] unityVector2s) + public static void ToUnityVector2s(this GameBoxVector2[] gameBoxVector2s, UnityEngine.Vector2[] unityVector2sBuffer) { - if (gameBoxVector2s == null || unityVector2s == null || gameBoxVector2s.Length != unityVector2s.Length) + if (gameBoxVector2s == null || unityVector2sBuffer == null || gameBoxVector2s.Length != unityVector2sBuffer.Length) { - throw new ArgumentException("gameBoxVector2s and unityVector2s must be non-null and have the same length"); + throw new ArgumentException("gameBoxVector2s and unityVector2sBuffer must be non-null and have the same length"); } for (var i = 0; i < gameBoxVector2s.Length; i++) { - unityVector2s[i] = gameBoxVector2s[i].ToUnityVector2(); + unityVector2sBuffer[i] = gameBoxVector2s[i].ToUnityVector2(); } } diff --git a/Assets/Scripts/Pal3.Core/DataReader/Cpk/CpkArchive.cs b/Assets/Scripts/Pal3.Core/DataReader/Cpk/CpkArchive.cs index e5a0e28e5..a768b943a 100644 --- a/Assets/Scripts/Pal3.Core/DataReader/Cpk/CpkArchive.cs +++ b/Assets/Scripts/Pal3.Core/DataReader/Cpk/CpkArchive.cs @@ -38,11 +38,6 @@ public sealed class CpkArchive public CpkArchive(string cpkFilePath, Crc32Hash crcHash, int codepage) { - if (!File.Exists(cpkFilePath)) - { - throw new FileNotFoundException($"游戏数据加载失败,游戏原CPK数据文件不存在: {cpkFilePath}"); - } - _filePath = cpkFilePath; _crcHash = crcHash; _codepage = codepage; diff --git a/Assets/Scripts/Pal3.Core/DataReader/Cpk/CpkTableEntity.cs b/Assets/Scripts/Pal3.Core/DataReader/Cpk/CpkTableEntity.cs index 4a83a09ad..b6ce9e3a2 100644 --- a/Assets/Scripts/Pal3.Core/DataReader/Cpk/CpkTableEntity.cs +++ b/Assets/Scripts/Pal3.Core/DataReader/Cpk/CpkTableEntity.cs @@ -19,7 +19,7 @@ public enum CpkTableEntityFlag IsDir = 0x2, // 是否是目录 IsLargeFile = 0x4, // 大文件 IsDeleted = 0x10, // 是否已删除 - IsNotCompressed = 0x10000, + IsNotCompressed = 0x10000, // 是否未压缩 }; // CPK 文件表结构 diff --git a/Assets/Scripts/Pal3.Core/DataReader/Dxt/Dxt1Decoder.cs b/Assets/Scripts/Pal3.Core/DataReader/Dxt/Dxt1Decoder.cs index c311e2fce..6a3687f1f 100644 --- a/Assets/Scripts/Pal3.Core/DataReader/Dxt/Dxt1Decoder.cs +++ b/Assets/Scripts/Pal3.Core/DataReader/Dxt/Dxt1Decoder.cs @@ -21,11 +21,11 @@ public static class Dxt1Decoder public static byte[] ToRgba32(byte[] data, int width, int height) { byte[] buffer = new byte[width * height * 4]; - ToRgba32NonAlloc(data, width, height, buffer); + ToRgba32(data, width, height, buffer); return buffer; } - public static unsafe void ToRgba32NonAlloc(byte[] data, int width, int height, byte[] buffer) + public static unsafe void ToRgba32(byte[] data, int width, int height, byte[] buffer) { if (buffer == null || buffer.Length < width * height * 4) { diff --git a/Assets/Scripts/Pal3.Core/DataReader/Dxt/Dxt3Decoder.cs b/Assets/Scripts/Pal3.Core/DataReader/Dxt/Dxt3Decoder.cs index 5f59c5b08..25b996b68 100644 --- a/Assets/Scripts/Pal3.Core/DataReader/Dxt/Dxt3Decoder.cs +++ b/Assets/Scripts/Pal3.Core/DataReader/Dxt/Dxt3Decoder.cs @@ -21,11 +21,11 @@ public static class Dxt3Decoder public static byte[] ToRgba32(byte[] data, int width, int height) { byte[] buffer = new byte[width * height * 4]; - ToRgba32NonAlloc(data, width, height, buffer); + ToRgba32(data, width, height, buffer); return buffer; } - public static unsafe void ToRgba32NonAlloc(byte[] data, int width, int height, byte[] buffer) + public static unsafe void ToRgba32(byte[] data, int width, int height, byte[] buffer) { if (buffer == null || buffer.Length < width * height * 4) { diff --git a/Assets/Scripts/Pal3.Core/DataReader/Tga.meta b/Assets/Scripts/Pal3.Core/DataReader/Tga.meta new file mode 100644 index 000000000..1a927197a --- /dev/null +++ b/Assets/Scripts/Pal3.Core/DataReader/Tga.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8ac2544082dc45e080ba5c566050dc5b +timeCreated: 1697370790 \ No newline at end of file diff --git a/Assets/Scripts/Pal3.Core/DataReader/Tga/TgaDecoder.cs b/Assets/Scripts/Pal3.Core/DataReader/Tga/TgaDecoder.cs new file mode 100644 index 000000000..8165a16f7 --- /dev/null +++ b/Assets/Scripts/Pal3.Core/DataReader/Tga/TgaDecoder.cs @@ -0,0 +1,86 @@ +// --------------------------------------------------------------------------------------------- +// Copyright (c) 2021-2023, Jiaqi Liu. All rights reserved. +// See LICENSE file in the project root for license information. +// --------------------------------------------------------------------------------------------- + +namespace Pal3.Core.DataReader.Tga +{ + using System; + + public static class TgaDecoder + { + private const int TGA_FILE_HEADER_SIZE = 18; + + public static byte[] Decode24BitDataToRgba32(byte[] data, + int width, + int height) + { + byte[] buffer = new byte[width * height * 4]; + Decode24BitDataToRgba32(data, width, height, buffer); + return buffer; + } + + public static unsafe void Decode24BitDataToRgba32(byte[] data, + int width, + int height, + byte[] buffer) + { + if (buffer == null || buffer.Length < width * height * 4) + { + throw new ArgumentException("buffer is null or too small"); + } + + fixed (byte* srcStart = &data[TGA_FILE_HEADER_SIZE], dstStart = buffer) + { + byte* src = srcStart, dst = dstStart; + for (var i = 0; i < width * height; i++, src += 3, dst += 4) + { + *dst = *(src + 2); + *(dst + 1) = *(src + 1); + *(dst + 2) = *src; + *(dst + 3) = 0; // 24-bit don't have alpha + } + } + } + + public static byte[] Decode32BitDataToRgba32(byte[] data, + int width, + int height, + out bool hasAlphaChannel) + { + byte[] buffer = new byte[width * height * 4]; + Decode32BitDataToRgba32(data, width, height, buffer, out hasAlphaChannel); + return buffer; + } + + public static unsafe void Decode32BitDataToRgba32(byte[] data, + int width, + int height, + byte[] buffer, + out bool hasAlphaChannel) + { + if (buffer == null || buffer.Length < width * height * 4) + { + throw new ArgumentException("buffer is null or too small"); + } + + hasAlphaChannel = false; + fixed (byte* srcStart = &data[TGA_FILE_HEADER_SIZE], dstStart = buffer) + { + var firstAlpha = *(srcStart + 3); + byte* src = srcStart, dst = dstStart; + for (var i = 0; i < width * height; i++, src += 4, dst += 4) + { + *dst = *(src + 2); + *(dst + 1) = *(src + 1); + *(dst + 2) = *src; + + byte alpha = *(src + 3); + *(dst + 3) = alpha; + + if (alpha != firstAlpha) hasAlphaChannel = true; + } + } + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Pal3.Core/DataReader/Tga/TgaDecoder.cs.meta b/Assets/Scripts/Pal3.Core/DataReader/Tga/TgaDecoder.cs.meta new file mode 100644 index 000000000..a5ed1c46e --- /dev/null +++ b/Assets/Scripts/Pal3.Core/DataReader/Tga/TgaDecoder.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9a31183d652645d4a2c5aac34b1eb8ad +timeCreated: 1697370803 \ No newline at end of file diff --git a/Assets/Scripts/Pal3.Core/FileSystem/CpkFileSystem.cs b/Assets/Scripts/Pal3.Core/FileSystem/CpkFileSystem.cs index 3bbf68f35..893dc20e1 100644 --- a/Assets/Scripts/Pal3.Core/FileSystem/CpkFileSystem.cs +++ b/Assets/Scripts/Pal3.Core/FileSystem/CpkFileSystem.cs @@ -32,7 +32,7 @@ public CpkFileSystem(string rootPath, Crc32Hash crcHash) if (!Directory.Exists(rootPath)) { - throw new DirectoryNotFoundException($"游戏数据加载失败,原始游戏数据根目录不存在: {rootPath}"); + throw new DirectoryNotFoundException(rootPath); } _rootPath = rootPath; @@ -51,14 +51,21 @@ public string GetRootPath() /// Codepage CPK file uses for encoding text info public void Mount(string cpkFileRelativePath, int codepage) { - var cpkFileName = CoreUtility.GetFileName(cpkFileRelativePath, Path.DirectorySeparatorChar).ToLower(); + string cpkFileName = CoreUtility.GetFileName(cpkFileRelativePath, Path.DirectorySeparatorChar).ToLower(); if (_cpkArchives.ContainsKey(cpkFileName)) { return; } - var cpkArchive = new CpkArchive(_rootPath + cpkFileRelativePath, _crcHash, codepage); + string cpkFilePath = _rootPath + cpkFileRelativePath; + + if (!File.Exists(cpkFilePath)) + { + throw new FileNotFoundException(cpkFilePath); + } + + var cpkArchive = new CpkArchive(cpkFilePath, _crcHash, codepage); cpkArchive.Init(); _cpkArchives[cpkFileName] = cpkArchive; } diff --git a/Assets/Scripts/Pal3.Game/GameResourceInitializer.cs b/Assets/Scripts/Pal3.Game/GameResourceInitializer.cs index c85bffd50..11964d98c 100644 --- a/Assets/Scripts/Pal3.Game/GameResourceInitializer.cs +++ b/Assets/Scripts/Pal3.Game/GameResourceInitializer.cs @@ -149,7 +149,15 @@ private IEnumerator InitResourceAsync() } else { - loadingText.text = exception == null ? "游戏数据加载失败,未能找到原游戏数据文件" : $"{exception.Message}"; + string errorMessage = exception switch + { + null => "游戏数据加载失败,未能找到原游戏数据文件", + DirectoryNotFoundException => $"游戏数据加载失败,原始游戏数据根目录不存在: {exception.Message}", + FileNotFoundException => $"游戏数据加载失败,游戏原CPK数据文件不存在: {exception.Message}", + _ => $"游戏数据加载失败,错误信息:{exception}" + }; + + loadingText.text = errorMessage; yield break; // Stop initialization if failed to init file system } } diff --git a/Assets/Scripts/Pal3.Game/Rendering/Renderer/CvdModelRenderer.cs b/Assets/Scripts/Pal3.Game/Rendering/Renderer/CvdModelRenderer.cs index b47a8bec0..2a8f7f0c5 100644 --- a/Assets/Scripts/Pal3.Game/Rendering/Renderer/CvdModelRenderer.cs +++ b/Assets/Scripts/Pal3.Game/Rendering/Renderer/CvdModelRenderer.cs @@ -241,7 +241,7 @@ private void RenderMeshInternal( meshSection.GameBoxTriangles.Length); // Triangles are the same for all frames, so we can just copy them once. - meshSection.GameBoxTriangles.ToUnityTrianglesNonAlloc(meshDataBuffer.TriangleBuffer); + meshSection.GameBoxTriangles.ToUnityTriangles(meshDataBuffer.TriangleBuffer); UpdateMeshDataBuffer(ref meshDataBuffer, meshSection, diff --git a/Assets/Scripts/Pal3.Game/Rendering/Renderer/Mv3ModelRenderer.cs b/Assets/Scripts/Pal3.Game/Rendering/Renderer/Mv3ModelRenderer.cs index 8d322235c..9e0777479 100644 --- a/Assets/Scripts/Pal3.Game/Rendering/Renderer/Mv3ModelRenderer.cs +++ b/Assets/Scripts/Pal3.Game/Rendering/Renderer/Mv3ModelRenderer.cs @@ -242,17 +242,17 @@ private void InitSubMeshes(int index, uvBufferSize: mv3Mesh.Uvs.Length, triangleBufferSize: mv3Mesh.GameBoxTriangles.Length); - mv3Mesh.KeyFrames[0].GameBoxVertices.ToUnityPositionsNonAlloc( + mv3Mesh.KeyFrames[0].GameBoxVertices.ToUnityPositions( _renderMeshComponents[index].MeshDataBuffer.VertexBuffer, UnityPrimitivesConvertor.GameBoxMv3UnitToUnityUnit); - mv3Mesh.GameBoxTriangles.ToUnityTrianglesNonAlloc( + mv3Mesh.GameBoxTriangles.ToUnityTriangles( _renderMeshComponents[index].MeshDataBuffer.TriangleBuffer); - mv3Mesh.GameBoxNormals.ToUnityNormalsNonAlloc( + mv3Mesh.GameBoxNormals.ToUnityNormals( _renderMeshComponents[index].MeshDataBuffer.NormalBuffer); - mv3Mesh.Uvs.ToUnityVector2sNonAlloc( + mv3Mesh.Uvs.ToUnityVector2s( _renderMeshComponents[index].MeshDataBuffer.UvBuffer); Mesh renderMesh = meshRenderer.Render(