Compare commits
47 Commits
80929039cc
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| a5aee0a464 | |||
| dfaf911b57 | |||
| 5b02733d20 | |||
| 11336807d7 | |||
| c05dae9429 | |||
| 23b14ac69f | |||
| c9fdd30185 | |||
| db0bdc8618 | |||
| a87ff6d2db | |||
| 3f6d131d0d | |||
| 4c71a20935 | |||
| 49a729078c | |||
| f0160ce7c2 | |||
| b78732f30c | |||
| 86353305e8 | |||
| d1f4a37097 | |||
| 1a36a8b6d7 | |||
| eaa7814b18 | |||
| 49c9b00c11 | |||
| 4072285425 | |||
| 0ce752b7ba | |||
| 1de0196f26 | |||
| c2da274a11 | |||
| c9d369d2c4 | |||
| 54fed7e37b | |||
| e73f649187 | |||
| 2f9da2e852 | |||
| 8dbce47d8a | |||
| d31a98a859 | |||
| 9c4c4a7fa4 | |||
| cf0966e6d3 | |||
| 6ea43cd82f | |||
| 4f24b76193 | |||
| 518d2c77ec | |||
| 9aa5e05a03 | |||
| a04a3a9b34 | |||
| 8e0c6f3793 | |||
| 763255d2a7 | |||
| ade1eadd50 | |||
| 4619cb5d1a | |||
| 3310cac100 | |||
| fdf2a4fc22 | |||
| 5fe62e8fb3 | |||
| 1b991dd834 | |||
| 30f7201a07 | |||
| 9917db0399 | |||
| a30a0a41d7 |
13
.github/scripts/windows.bat
vendored
13
.github/scripts/windows.bat
vendored
@@ -9,6 +9,19 @@ MKDIR install
|
||||
|
||||
:: Build with x64 architecture in Release mode
|
||||
CD build
|
||||
:: We set this to revert the incompatible ABI for MSVC STL.
|
||||
:: See: https://github.com/microsoft/STL/wiki/VS-2022-Changelog#vs-2022-1710
|
||||
::
|
||||
:: Ideally, I can install new VCRedist to resolve this issue.
|
||||
:: However, Blender embeds its own VCRedist when distribution which has lower VCRedist version.
|
||||
:: And at the same time, Blender will load our BMap.dll built by new VCRedist, so it trigger the incompatible ABI issue.
|
||||
::
|
||||
:: Currently, the VCRedist distributed by Blender 4.5 LTS is incompatible with our BMap.dll.
|
||||
:: So I write it in script, rather than in CMake file, to temporaryly fix this issue.
|
||||
::
|
||||
:: This fix also is written in dependency build scripts, please remove them together if you remove this,
|
||||
:: when Blender migrate to the new VCRedist.
|
||||
set CXXFLAGS=/D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR=1
|
||||
cmake -A x64 -DCMAKE_CXX_STANDARD=23 -DNEMO_BUILD_UNVIRT=ON -DNEMO_BUILD_BALLANCE=ON -DNEMO_BUILD_BMAP=ON -DNEMO_BUILD_BMAPINSPECTOR=ON -DYYCCommonplace_ROOT=%YYCCommonplace_ROOT% -DSTB_ROOT=%STB_ROOT% -DZLIB_ROOT=%ZLIB_ROOT% ../..
|
||||
cmake --build . --config Release
|
||||
cmake --install . --prefix=../install --config Release
|
||||
|
||||
1
.github/scripts/yycc/windows.bat
vendored
1
.github/scripts/yycc/windows.bat
vendored
@@ -9,6 +9,7 @@ MKDIR install
|
||||
|
||||
:: Build with x64 architecture in Release mode
|
||||
CD build
|
||||
set CXXFLAGS=/D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR=1
|
||||
cmake -A x64 -DCMAKE_CXX_STANDARD=23 ../..
|
||||
cmake --build . --config Release
|
||||
cmake --install . --prefix=../install --config Release
|
||||
|
||||
2
.github/workflows/linux.yml
vendored
2
.github/workflows/linux.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'yyc12345/YYCCommonplace'
|
||||
ref: 'master'
|
||||
ref: 'v2.0.0'
|
||||
path: 'extern/YYCCommonplace'
|
||||
- name: Build YYCCommonplace
|
||||
shell: bash
|
||||
|
||||
2
.github/workflows/macos.yml
vendored
2
.github/workflows/macos.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'yyc12345/YYCCommonplace'
|
||||
ref: 'master'
|
||||
ref: 'v2.0.0'
|
||||
path: 'extern/YYCCommonplace'
|
||||
- name: Build YYCCommonplace
|
||||
shell: bash
|
||||
|
||||
2
.github/workflows/windows.yml
vendored
2
.github/workflows/windows.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'yyc12345/YYCCommonplace'
|
||||
ref: 'master'
|
||||
ref: 'v2.0.0'
|
||||
path: 'extern/YYCCommonplace'
|
||||
- name: Build YYCCommonplace
|
||||
shell: cmd
|
||||
|
||||
@@ -5,9 +5,12 @@
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
|
||||
<PackageId>BMapSharp</PackageId>
|
||||
<Version>1.0.0</Version>
|
||||
<Title>BMapSharp</Title>
|
||||
<Version>0.4.0</Version>
|
||||
<Authors>yyc12345</Authors>
|
||||
<Description>The C# binding to BMap.</Description>
|
||||
<Company>BearKidsTeam</Company>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="$([System.OperatingSystem]::IsWindows())">
|
||||
|
||||
@@ -269,6 +269,16 @@ namespace BMapSharp.BMapWrapper {
|
||||
public float GetSpecularPower() => GetGenericValue<float>(BMap.BMMaterial_GetSpecularPower);
|
||||
public void SetSpecularPower(float val) => SetGenericValue<float>(BMap.BMMaterial_SetSpecularPower, val);
|
||||
|
||||
public BMTexture GetTexture() {
|
||||
BMapException.ThrowIfFailed(BMap.BMMaterial_GetTexture(GetPointer(), GetCKID(), out uint out_texid));
|
||||
if (out_texid == Utils.INVALID_CKID) return null;
|
||||
else return new BMTexture(GetPointer(), out_texid);
|
||||
}
|
||||
public void SetTexture(BMTexture tex) {
|
||||
uint texid = (tex is null) ? Utils.INVALID_CKID : tex.GetCKID();
|
||||
BMapException.ThrowIfFailed(BMap.BMMaterial_SetTexture(GetPointer(), GetCKID(), texid));
|
||||
}
|
||||
|
||||
public VxColor GetTextureBorderColor() {
|
||||
BMapException.ThrowIfFailed(BMap.BMMaterial_GetTextureBorderColor(GetPointer(), GetCKID(), out uint out_val));
|
||||
return new VxColor(out_val);
|
||||
@@ -515,7 +525,7 @@ namespace BMapSharp.BMapWrapper {
|
||||
BMapException.ThrowIfFailed(fct_cnt(this.GetPointer(), out uint out_count));
|
||||
return out_count;
|
||||
}
|
||||
private IEnumerable<T> GetGenericObject<T>(FctProtoGetCount fct_cnt, FctProtoGetObject fct_obj, FctProtoCreateInstance<T> fct_crt) {
|
||||
private IEnumerable<T> GetGenericObjects<T>(FctProtoGetCount fct_cnt, FctProtoGetObject fct_obj, FctProtoCreateInstance<T> fct_crt) {
|
||||
uint count = GetGenericObjectCount(fct_cnt);
|
||||
for (uint i = 0; i < count; ++i) {
|
||||
BMapException.ThrowIfFailed(fct_obj(this.GetPointer(), i, out uint out_id));
|
||||
@@ -526,31 +536,31 @@ namespace BMapSharp.BMapWrapper {
|
||||
public uint GetTextureCount() =>
|
||||
GetGenericObjectCount(BMap.BMFile_GetTextureCount);
|
||||
public IEnumerable<BMTexture> GetTextures() =>
|
||||
GetGenericObject<BMTexture>(BMap.BMFile_GetTextureCount, BMap.BMFile_GetTexture, (bmf, id) => new BMTexture(bmf, id));
|
||||
GetGenericObjects<BMTexture>(BMap.BMFile_GetTextureCount, BMap.BMFile_GetTexture, (bmf, id) => new BMTexture(bmf, id));
|
||||
public uint GetMaterialCount() =>
|
||||
GetGenericObjectCount(BMap.BMFile_GetMaterialCount);
|
||||
public IEnumerable<BMMaterial> GetMaterials() =>
|
||||
GetGenericObject<BMMaterial>(BMap.BMFile_GetMaterialCount, BMap.BMFile_GetMaterial, (bmf, id) => new BMMaterial(bmf, id));
|
||||
GetGenericObjects<BMMaterial>(BMap.BMFile_GetMaterialCount, BMap.BMFile_GetMaterial, (bmf, id) => new BMMaterial(bmf, id));
|
||||
public uint GetMeshCount() =>
|
||||
GetGenericObjectCount(BMap.BMFile_GetMeshCount);
|
||||
public IEnumerable<BMMesh> GetMeshes() =>
|
||||
GetGenericObject<BMMesh>(BMap.BMFile_GetMeshCount, BMap.BMFile_GetMesh, (bmf, id) => new BMMesh(bmf, id));
|
||||
GetGenericObjects<BMMesh>(BMap.BMFile_GetMeshCount, BMap.BMFile_GetMesh, (bmf, id) => new BMMesh(bmf, id));
|
||||
public uint Get3dObjectCount() =>
|
||||
GetGenericObjectCount(BMap.BMFile_Get3dObjectCount);
|
||||
public IEnumerable<BM3dObject> Get3dObjects() =>
|
||||
GetGenericObject<BM3dObject>(BMap.BMFile_Get3dObjectCount, BMap.BMFile_Get3dObject, (bmf, id) => new BM3dObject(bmf, id));
|
||||
GetGenericObjects<BM3dObject>(BMap.BMFile_Get3dObjectCount, BMap.BMFile_Get3dObject, (bmf, id) => new BM3dObject(bmf, id));
|
||||
public uint GetGroupCount() =>
|
||||
GetGenericObjectCount(BMap.BMFile_GetGroupCount);
|
||||
public IEnumerable<BMGroup> GetGroups() =>
|
||||
GetGenericObject<BMGroup>(BMap.BMFile_GetGroupCount, BMap.BMFile_GetGroup, (bmf, id) => new BMGroup(bmf, id));
|
||||
GetGenericObjects<BMGroup>(BMap.BMFile_GetGroupCount, BMap.BMFile_GetGroup, (bmf, id) => new BMGroup(bmf, id));
|
||||
public uint GetTargetLightCount() =>
|
||||
GetGenericObjectCount(BMap.BMFile_GetTargetLightCount);
|
||||
public IEnumerable<BMTargetLight> GetTargetLights() =>
|
||||
GetGenericObject<BMTargetLight>(BMap.BMFile_GetTargetLightCount, BMap.BMFile_GetTargetLight, (bmf, id) => new BMTargetLight(bmf, id));
|
||||
GetGenericObjects<BMTargetLight>(BMap.BMFile_GetTargetLightCount, BMap.BMFile_GetTargetLight, (bmf, id) => new BMTargetLight(bmf, id));
|
||||
public uint GetTargetCameraCount() =>
|
||||
GetGenericObjectCount(BMap.BMFile_GetTargetCameraCount);
|
||||
public IEnumerable<BMTargetCamera> GetTargetCameras() =>
|
||||
GetGenericObject<BMTargetCamera>(BMap.BMFile_GetTargetCameraCount, BMap.BMFile_GetTargetCamera, (bmf, id) => new BMTargetCamera(bmf, id));
|
||||
GetGenericObjects<BMTargetCamera>(BMap.BMFile_GetTargetCameraCount, BMap.BMFile_GetTargetCamera, (bmf, id) => new BMTargetCamera(bmf, id));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace BMapSharpTest {
|
||||
if (ballance_dir is null) {
|
||||
throw new CliException("You must specify BMAP_BALLANCE_DIR environment variable before running this test.");
|
||||
}
|
||||
this.BallanceDirectory = ballance_dir;
|
||||
this.BallanceDir = ballance_dir;
|
||||
|
||||
var encodings = System.Environment.GetEnvironmentVariable("BMAP_ENCODINGS");
|
||||
if (encodings is null) {
|
||||
@@ -42,7 +42,7 @@ namespace BMapSharpTest {
|
||||
/// <summary>
|
||||
/// The path to the Ballance directory for finding textures
|
||||
/// </summary>
|
||||
public string BallanceDirectory { get; private set; }
|
||||
public string BallanceDir { get; private set; }
|
||||
/// <summary>
|
||||
/// The name of encodings used by BMap for loading map.
|
||||
/// </summary>
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace BMapSharpTest {
|
||||
string file_name = cli.FileName;
|
||||
var temp_dir_info = Directory.CreateTempSubdirectory();
|
||||
string temp_dir = temp_dir_info.FullName;
|
||||
string texture_dir = Path.Combine(cli.BallanceDirectory, "Textures");
|
||||
string texture_dir = Path.Combine(cli.BallanceDir, "Textures");
|
||||
string[] encodings = cli.Encodings;
|
||||
|
||||
using (var reader = new BMapSharp.BMapWrapper.BMFileReader(file_name, temp_dir, texture_dir, encodings)) {
|
||||
|
||||
@@ -68,6 +68,10 @@ namespace BMapSharpTest.TestSuits {
|
||||
|
||||
Console.WriteLine($"\tSpecular Power: {mtl.GetSpecularPower()}");
|
||||
|
||||
var current_texture = mtl.GetTexture();
|
||||
var texture_name = current_texture is null ? "<null>" : current_texture.GetName();
|
||||
Console.WriteLine($"\tTexture: {texture_name}");
|
||||
|
||||
Console.WriteLine($"\tTexture Border Color: {mtl.GetTextureBorderColor().ToManagedRGBA()}");
|
||||
|
||||
Console.WriteLine($"\tTexture Blend Mode: {mtl.GetTextureBlendMode()}");
|
||||
@@ -108,15 +112,15 @@ namespace BMapSharpTest.TestSuits {
|
||||
Console.WriteLine($"\tVisibility: {lit.GetVisibility()}");
|
||||
Console.WriteLine($"\tMatrix: {lit.GetWorldMatrix().ToManaged()}");
|
||||
|
||||
Console.WriteLine($"Type: {lit.GetLightType()}");
|
||||
Console.WriteLine($"Color: {lit.GetColor().ToManagedRGBA()}");
|
||||
Console.WriteLine($"Constant Attenuation: {lit.GetConstantAttenuation()}");
|
||||
Console.WriteLine($"Linear Attenuation: {lit.GetLinearAttenuation()}");
|
||||
Console.WriteLine($"Quadratic Attenuation: {lit.GetQuadraticAttenuation()}");
|
||||
Console.WriteLine($"Range: {lit.GetRange()}");
|
||||
Console.WriteLine($"Hot Spot: {lit.GetHotSpot()}");
|
||||
Console.WriteLine($"Falloff: {lit.GetFalloff()}");
|
||||
Console.WriteLine($"Falloff Shape: {lit.GetFalloffShape()}");
|
||||
Console.WriteLine($"\tType: {lit.GetLightType()}");
|
||||
Console.WriteLine($"\tColor: {lit.GetColor().ToManagedRGBA()}");
|
||||
Console.WriteLine($"\tConstant Attenuation: {lit.GetConstantAttenuation()}");
|
||||
Console.WriteLine($"\tLinear Attenuation: {lit.GetLinearAttenuation()}");
|
||||
Console.WriteLine($"\tQuadratic Attenuation: {lit.GetQuadraticAttenuation()}");
|
||||
Console.WriteLine($"\tRange: {lit.GetRange()}");
|
||||
Console.WriteLine($"\tHot Spot: {lit.GetHotSpot()}");
|
||||
Console.WriteLine($"\tFalloff: {lit.GetFalloff()}");
|
||||
Console.WriteLine($"\tFalloff Shape: {lit.GetFalloffShape()}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,14 +131,14 @@ namespace BMapSharpTest.TestSuits {
|
||||
Console.WriteLine($"\tVisibility: {cam.GetVisibility()}");
|
||||
Console.WriteLine($"\tMatrix: {cam.GetWorldMatrix().ToManaged()}");
|
||||
|
||||
Console.WriteLine($"Type: {cam.GetProjectionType()}");
|
||||
Console.WriteLine($"Orthographic Zoom: {cam.GetOrthographicZoom()}");
|
||||
Console.WriteLine($"Front Plane: {cam.GetFrontPlane()}");
|
||||
Console.WriteLine($"Back Plane: {cam.GetBackPlane()}");
|
||||
Console.WriteLine($"Fov: {cam.GetFov()}");
|
||||
Console.WriteLine($"\tType: {cam.GetProjectionType()}");
|
||||
Console.WriteLine($"\tOrthographic Zoom: {cam.GetOrthographicZoom()}");
|
||||
Console.WriteLine($"\tFront Plane: {cam.GetFrontPlane()}");
|
||||
Console.WriteLine($"\tBack Plane: {cam.GetBackPlane()}");
|
||||
Console.WriteLine($"\tFov: {cam.GetFov()}");
|
||||
|
||||
cam.GetAspectRatio(out var width, out var height);
|
||||
Console.WriteLine($"Aspect Ratio: {width}:{height}");
|
||||
Console.WriteLine($"\tAspect Ratio: {width}:{height}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
# BMapSharp
|
||||
|
||||
The core of BMapSharp project is placed within `BMapSharp` subdirectory. This directory also contain a testbench project of `BMapSharp`, called `BMapSharpTest`. You can build it and do basic test for `BMapSharp`.
|
||||
## Layout
|
||||
|
||||
The native BMap library should be placed together with managed `BMapSharp` dynamic library. I use gitignore file to filter all native binary so you need put them manually. The native BMap library must be named as `BMap.dll` (in Windows), `BMap.so` (in Linux or BSD), or `BMap.dylib` (in macOS). If you still can not load BMap or your system is not listed above, you should name it as `BMap.bin`.
|
||||
This directory contains 2 project.
|
||||
`BMapSharp` is the binding to BMap and `BMapSharpTest` is the test for `BMapSharp`.
|
||||
|
||||
The most content of `VirtoolsTypes.cs` is generated by EnumsMigration, and the most content of `BMap.cs` is generated by BMapBindings. You should watch these file changes if corresponding C++ code or structures are changed.
|
||||
> [!NOTE]
|
||||
> `BMapSharpTest` project do not utilize any existing test framework.
|
||||
> The test should be executed manually by compiling it and running it.
|
||||
|
||||
Since BMap 0.3.0, testbench use command line arguments, instead of hardcode variables in code, as the arguments of BMap. It is convenient that debug BMapSharp without any modification of source code. For a brief instruction, you may need to launch BMapSharpTest in following command (just an example. you can modify it as you wished): `dotnet run -- --file-path "LightCameraTest.nmo" --temp-dir "Temp" --texture-dir "F:/Ballance/Ballance/Textures" --encodings cp1252 gb2312`.
|
||||
## Native BMap Library Location
|
||||
|
||||
The native BMap library should be placed together with managed `BMapSharp` dynamic library.
|
||||
|
||||
The native BMap library must be named as `BMap.dll` (in Windows), `BMap.so` (in Linux or BSD), `BMap.dylib` (in macOS), or `BMap.bin` (in any other platforms).
|
||||
|
||||
If you are building final distributed package file (I have not build them yet), you may need manually put native BMap library file into package file because I have not write this behavior in any place.
|
||||
|
||||
28
Assets/BMapBindings/README.md
Normal file
28
Assets/BMapBindings/README.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# BMap Bindings
|
||||
|
||||
This directory contains bindings for C++ written BMap for interacting with Ballance map.
|
||||
We now provide following bindings due to different reasons:
|
||||
|
||||
* pybmap: The Python bindings for BMap. [Ballance Blender Plugin](https://github.com/yyc12345/BallanceBlenderHelper) require this.
|
||||
* BMapSharp: The C# bindings for BMap. This can be used in C#-available game engine (Unity, Godot, etc.) for implementing next generation of Ballance with legacy map format support.
|
||||
* bmap-rs: The Rust bindings for BMap. ZZQ request it for his Ballance launcher.
|
||||
|
||||
## Package Principle
|
||||
|
||||
In theory, these BMap bindings can be distributed as the package in their corresponding package manager.
|
||||
However, considering these bindings is specific for a minor territory, Ballance mapping,
|
||||
and the complexity of building the essential artificate, dynamic library BMap written in C++,
|
||||
I give up the idea of distributing them as package.
|
||||
For all users who want to utilize these bindings, you should build them on their own,
|
||||
according to the build manual provided in respective directory.
|
||||
|
||||
## Test
|
||||
|
||||
These BMap bindings all have test for testing whether they work correctly.
|
||||
And these test program following the same pattern for fetching test parameters
|
||||
indluding where to find the map and etc.
|
||||
More preciously, you should set environment variables list following before running test programs.
|
||||
|
||||
* `BMAP_FILE_NAME`: The path to the map for loading.
|
||||
* `BMAP_BALLANCE_DIR`:The path to the Ballance directory for finding textures
|
||||
* `BMAP_ENCODINGS`: The name of encodings used by BMap for loading map. Multiple encodings is supported by separating them with commas, for example: `cp1252,gbk`.
|
||||
463
Assets/BMapBindings/bmap-rs/Cargo.lock
generated
Normal file
463
Assets/BMapBindings/bmap-rs/Cargo.lock
generated
Normal file
@@ -0,0 +1,463 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||
|
||||
[[package]]
|
||||
name = "bmap-rs"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasip2",
|
||||
"wasip3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "id-arena"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.16.1",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||
|
||||
[[package]]
|
||||
name = "leb128fmt"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.182"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.149"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.115"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e614ed320ac28113fa64972c4262d5dbc89deacdfd00c34a3e4cea073243c12"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "wasip2"
|
||||
version = "1.0.2+wasi-0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasip3"
|
||||
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
|
||||
dependencies = [
|
||||
"leb128fmt",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
"wasm-encoder",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"hashbrown 0.15.5",
|
||||
"indexmap",
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
|
||||
dependencies = [
|
||||
"wit-bindgen-rust-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-core"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"indexmap",
|
||||
"prettyplease",
|
||||
"syn",
|
||||
"wasm-metadata",
|
||||
"wit-bindgen-core",
|
||||
"wit-component",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust-macro"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wit-bindgen-core",
|
||||
"wit-bindgen-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-component"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
"indexmap",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"wasm-encoder",
|
||||
"wasm-metadata",
|
||||
"wasmparser",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-parser"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"id-arena",
|
||||
"indexmap",
|
||||
"log",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"unicode-xid",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "bmap-sys"
|
||||
name = "bmap-rs"
|
||||
version = "0.4.0"
|
||||
authors = ["yyc12345"]
|
||||
edition = "2024"
|
||||
@@ -7,3 +7,8 @@ description = "The Rust binding to BMap."
|
||||
license = "SPDX:MIT"
|
||||
|
||||
[dependencies]
|
||||
thiserror = "2.0.12"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.26.0"
|
||||
|
||||
43
Assets/BMapBindings/bmap-rs/README.md
Normal file
43
Assets/BMapBindings/bmap-rs/README.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# bmap-rs
|
||||
|
||||
## Layout
|
||||
|
||||
This project follows standard Rust project layout.
|
||||
Source code are located in `src` directory and test code are in `tests` directory.
|
||||
|
||||
Please note that the raw FFI functions and the wrapper of it are placed in the single project,
|
||||
not like other Rust project that raw bindings and wrapper are put into 2 different project,
|
||||
one of them is ended with `-sys` and another one is not.
|
||||
This is a considerable decision due to the following reasons:
|
||||
|
||||
- I want this project has same pattern with other bindings. In other bindings, FFI binding code and wrapper are put together.
|
||||
- They (FFI binding and wrapper) can not be easily splitted due to logic reasons. Putting them together can avoid some extra work.
|
||||
|
||||
## Native BMap Library Location
|
||||
|
||||
This project uses native way (native compiler and linker) to link the BMap library.
|
||||
This is different with other bindings.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> We highly suggest that you make sure that the toolchain building your BMap is same as your configured Rust toolchain.
|
||||
> Although BMap is a dynamic library and its function is exposed as C function, the link issue may still occurs due to the different toolchain.
|
||||
|
||||
According to this, you should set `LibCmo_ROOT` environment variable pointing to the CMake install directory of LibCmo with built BMap before configuring this Rust project.
|
||||
This project will find it in `build.rs` script and tell Rust compiler how to link it.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> For Linux and macOS user, you may need manually to rename the final built BMap artifact.
|
||||
> Because in these platforms, CMake produced BMap dynamic library may have `lib` prefix in its name.
|
||||
> This can not be recognized by our build script.
|
||||
>
|
||||
> You should rename it to `BMap.so` or `BMap.dylib` depending on your platform.
|
||||
> You also may need rename some contents of other files involving this rename change.
|
||||
|
||||
Also due to this, when distributing your Rust project, please do not forget copy the built BMap library with your Rust artifacts.
|
||||
|
||||
## Test
|
||||
|
||||
The test code located in `tests` directory is NOT designed for plain "cargo test".
|
||||
It is more close to an independent test program, so executing it may require some extra work.
|
||||
First you need configure all environment variables for testing.
|
||||
Then you can execute `cargo test complete -- --nocapture` to run the complete test program.
|
||||
1865
Assets/BMapBindings/bmap-rs/src/bmap.rs
Normal file
1865
Assets/BMapBindings/bmap-rs/src/bmap.rs
Normal file
File diff suppressed because it is too large
Load Diff
1864
Assets/BMapBindings/bmap-rs/src/bmap_wrapper.rs
Normal file
1864
Assets/BMapBindings/bmap-rs/src/bmap_wrapper.rs
Normal file
File diff suppressed because it is too large
Load Diff
113
Assets/BMapBindings/bmap-rs/src/bmap_wrapper/marshaler.rs
Normal file
113
Assets/BMapBindings/bmap-rs/src/bmap_wrapper/marshaler.rs
Normal file
@@ -0,0 +1,113 @@
|
||||
//! BMap wrapper used marshaler for string and string array.
|
||||
|
||||
use crate::bmap::{CKSTRING, PCKSTRING};
|
||||
use std::ffi::{CStr, CString};
|
||||
use thiserror::Error as TeError;
|
||||
|
||||
// region: Error and Result Types
|
||||
|
||||
/// Any possible error occurs in this module.
|
||||
#[derive(Debug, TeError, Clone)]
|
||||
pub enum Error {
|
||||
#[error("can not parse from native string")]
|
||||
FromNative(#[from] std::str::Utf8Error),
|
||||
#[error("can not format into native string")]
|
||||
ToNative(#[from] std::ffi::NulError),
|
||||
}
|
||||
|
||||
/// The result type used in this module.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
// endregion
|
||||
|
||||
pub unsafe fn from_native_string(ptr: CKSTRING) -> Result<String> {
|
||||
let s = unsafe { CStr::from_ptr(ptr) };
|
||||
Ok(String::from(s.to_str()?))
|
||||
}
|
||||
|
||||
pub unsafe fn to_native_string<T>(words: T) -> Result<BMString>
|
||||
where
|
||||
T: Into<Vec<u8>>,
|
||||
{
|
||||
BMString::new(words)
|
||||
}
|
||||
|
||||
pub struct BMString {
|
||||
inner: CString,
|
||||
}
|
||||
|
||||
impl BMString {
|
||||
fn new<T>(words: T) -> Result<Self>
|
||||
where
|
||||
T: Into<Vec<u8>>,
|
||||
{
|
||||
Ok(Self {
|
||||
inner: CString::new(words)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub unsafe fn as_raw(&self) -> CKSTRING {
|
||||
self.inner.as_ptr() as CKSTRING
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn from_native_string_array(ptr: PCKSTRING) -> Result<Vec<String>> {
|
||||
let mut rv = Vec::new();
|
||||
|
||||
loop {
|
||||
let item_ptr = unsafe { *ptr } as CKSTRING;
|
||||
if item_ptr.is_null() {
|
||||
break;
|
||||
}
|
||||
|
||||
let item = unsafe { from_native_string(item_ptr)? };
|
||||
rv.push(item);
|
||||
}
|
||||
|
||||
Ok(rv)
|
||||
}
|
||||
|
||||
pub unsafe fn to_native_string_array<I, T>(words: I) -> Result<BMStringArray>
|
||||
where
|
||||
I: Iterator<Item = T>,
|
||||
T: Into<Vec<u8>>,
|
||||
{
|
||||
BMStringArray::new(words)
|
||||
}
|
||||
|
||||
pub struct BMStringArray {
|
||||
array_items: Vec<CString>,
|
||||
array_body: Vec<CKSTRING>,
|
||||
}
|
||||
|
||||
impl BMStringArray {
|
||||
fn new<I, T>(words: I) -> Result<Self>
|
||||
where
|
||||
I: Iterator<Item = T>,
|
||||
T: Into<Vec<u8>>,
|
||||
{
|
||||
// Build array items
|
||||
let array_items = words
|
||||
.map(|word| CString::new(word))
|
||||
.collect::<std::result::Result<Vec<CString>, std::ffi::NulError>>()?;
|
||||
// Build array body.
|
||||
// In theory, move operation will not affect data allocated on heap.
|
||||
// So we can simply fetch the address of this new generated array.
|
||||
let array_body = array_items
|
||||
.iter()
|
||||
.map(|i| i.as_ptr() as CKSTRING)
|
||||
.chain(std::iter::once(std::ptr::null_mut() as CKSTRING))
|
||||
.collect::<Vec<CKSTRING>>();
|
||||
|
||||
// Return value
|
||||
Ok(Self { array_items, array_body })
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.array_items.len()
|
||||
}
|
||||
|
||||
pub unsafe fn as_raw(&self) -> PCKSTRING {
|
||||
self.array_body.as_ptr() as PCKSTRING
|
||||
}
|
||||
}
|
||||
4
Assets/BMapBindings/bmap-rs/src/lib.rs
Normal file
4
Assets/BMapBindings/bmap-rs/src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
//! The Rust binding to BMap.
|
||||
pub mod virtools_types;
|
||||
pub mod bmap;
|
||||
pub mod bmap_wrapper;
|
||||
480
Assets/BMapBindings/bmap-rs/src/virtools_types.rs
Normal file
480
Assets/BMapBindings/bmap-rs/src/virtools_types.rs
Normal file
@@ -0,0 +1,480 @@
|
||||
//! The module conatins all basic Virtools types used by BMap native FFI calling.
|
||||
|
||||
// region: Structures
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||||
#[repr(C)]
|
||||
#[repr(packed(4))]
|
||||
pub struct VxVector2 {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
}
|
||||
|
||||
impl VxVector2 {
|
||||
pub fn new() -> Self {
|
||||
Self::with_xy(0.0, 0.0)
|
||||
}
|
||||
|
||||
pub fn with_xy(x: f32, y: f32) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||||
#[repr(C)]
|
||||
#[repr(packed(4))]
|
||||
pub struct VxVector3 {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub z: f32,
|
||||
}
|
||||
|
||||
impl VxVector3 {
|
||||
pub fn new() -> Self {
|
||||
Self::with_xyz(0.0, 0.0, 0.0)
|
||||
}
|
||||
|
||||
pub fn with_xyz(x: f32, y: f32, z: f32) -> Self {
|
||||
Self { x, y, z }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||||
#[repr(C)]
|
||||
#[repr(packed(4))]
|
||||
pub struct VxColor {
|
||||
pub r: f32,
|
||||
pub g: f32,
|
||||
pub b: f32,
|
||||
pub a: f32,
|
||||
}
|
||||
|
||||
impl VxColor {
|
||||
pub fn new() -> Self {
|
||||
Self::with_rgba(0.0, 0.0, 0.0, 0.0)
|
||||
}
|
||||
|
||||
pub fn with_rgba(r: f32, g: f32, b: f32, a: f32) -> Self {
|
||||
let mut rv = Self { r, g, b, a };
|
||||
rv.regulate();
|
||||
return rv;
|
||||
}
|
||||
|
||||
pub fn with_rgb(r: f32, g: f32, b: f32) -> Self {
|
||||
Self::with_rgba(r, g, b, 1.0)
|
||||
}
|
||||
|
||||
pub fn with_dword(val: u32) -> Self {
|
||||
let mut rv = Self::new();
|
||||
rv.from_dword(val);
|
||||
return rv;
|
||||
}
|
||||
|
||||
pub fn from_dword(&mut self, mut val: u32) {
|
||||
self.b = (val & 0xFF) as f32 / 255.0;
|
||||
val >>= 8;
|
||||
self.g = (val & 0xFF) as f32 / 255.0;
|
||||
val >>= 8;
|
||||
self.r = (val & 0xFF) as f32 / 255.0;
|
||||
val >>= 8;
|
||||
self.a = (val & 0xFF) as f32 / 255.0;
|
||||
}
|
||||
|
||||
pub fn to_dword(&self) -> u32 {
|
||||
// Make a copy and regulate self first
|
||||
let mut copied = self.clone();
|
||||
copied.regulate();
|
||||
// Build result
|
||||
let mut rv = 0;
|
||||
rv |= (copied.b * 255.0) as u32;
|
||||
rv <<= 8;
|
||||
rv |= (copied.g * 255.0) as u32;
|
||||
rv <<= 8;
|
||||
rv |= (copied.r * 255.0) as u32;
|
||||
rv <<= 8;
|
||||
rv |= (copied.a * 255.0) as u32;
|
||||
return rv;
|
||||
}
|
||||
|
||||
fn clamp_factor(factor: f32) -> f32 {
|
||||
factor.min(1.0).max(0.0)
|
||||
}
|
||||
|
||||
pub fn regulate(&mut self) {
|
||||
self.r = Self::clamp_factor(self.r);
|
||||
self.g = Self::clamp_factor(self.g);
|
||||
self.b = Self::clamp_factor(self.b);
|
||||
self.a = Self::clamp_factor(self.a);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||||
#[repr(C)]
|
||||
#[repr(packed(4))]
|
||||
pub struct VxMatrix {
|
||||
pub data: [[f32; 4]; 4],
|
||||
}
|
||||
|
||||
impl VxMatrix {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
data: [
|
||||
[1.0, 0.0, 0.0, 0.0],
|
||||
[0.0, 1.0, 0.0, 0.0],
|
||||
[0.0, 0.0, 1.0, 0.0],
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub fn reset(&mut self) {
|
||||
self.data[0][0] = 1.0; self.data[0][1] = 0.0; self.data[0][2] = 0.0; self.data[0][3] = 0.0;
|
||||
self.data[1][0] = 0.0; self.data[1][1] = 1.0; self.data[1][2] = 0.0; self.data[1][3] = 0.0;
|
||||
self.data[2][0] = 0.0; self.data[2][1] = 0.0; self.data[2][2] = 1.0; self.data[2][3] = 0.0;
|
||||
self.data[3][0] = 0.0; self.data[3][1] = 0.0; self.data[3][2] = 0.0; self.data[3][3] = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||||
#[repr(C)]
|
||||
#[repr(packed(4))]
|
||||
pub struct CKFaceIndices {
|
||||
pub i1: u32,
|
||||
pub i2: u32,
|
||||
pub i3: u32,
|
||||
}
|
||||
|
||||
impl CKFaceIndices {
|
||||
pub fn new() -> Self {
|
||||
Self::with_indices(0, 0, 0)
|
||||
}
|
||||
|
||||
pub fn with_indices(i1: u32, i2: u32, i3: u32) -> Self {
|
||||
Self { i1, i2, i3 }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||||
#[repr(C)]
|
||||
#[repr(packed(4))]
|
||||
pub struct CKShortFaceIndices {
|
||||
pub i1: u16,
|
||||
pub i2: u16,
|
||||
pub i3: u16,
|
||||
}
|
||||
|
||||
impl CKShortFaceIndices {
|
||||
pub fn new() -> Self {
|
||||
Self::with_indices(0, 0, 0)
|
||||
}
|
||||
|
||||
pub fn with_indices(i1: u16, i2: u16, i3: u16) -> Self {
|
||||
Self { i1, i2, i3 }
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region: Enums
|
||||
|
||||
/// Specify the way textures or sprites will be saved
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[repr(u32)]
|
||||
#[non_exhaustive]
|
||||
#[rustfmt::skip]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum CK_TEXTURE_SAVEOPTIONS {
|
||||
/// Save raw data inside file. The bitmap is saved in a raw 32 bit per pixel format.
|
||||
CKTEXTURE_RAWDATA = 0,
|
||||
/// Store only the file name for the texture. The bitmap file must be present in the bitmap pathswhen loading the composition.
|
||||
CKTEXTURE_EXTERNAL = 1,
|
||||
/// Save using format specified. The bitmap data will be converted to thespecified format by the correspondant bitmap plugin and saved inside file.
|
||||
CKTEXTURE_IMAGEFORMAT = 2,
|
||||
/// Use Global settings, that is the settings given with CKContext::SetGlobalImagesSaveOptions. (Not valid when using CKContext::SetImagesSaveOptions).
|
||||
CKTEXTURE_USEGLOBAL = 3,
|
||||
/// Insert original image file inside CMO file. The bitmap file thatwas used originally for the texture or sprite will be append tothe composition file and extracted when the file is loaded.
|
||||
CKTEXTURE_INCLUDEORIGINALFILE = 4,
|
||||
}
|
||||
|
||||
/// Pixel format types.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[repr(u32)]
|
||||
#[non_exhaustive]
|
||||
#[rustfmt::skip]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum VX_PIXELFORMAT {
|
||||
/// Unknown pixel format
|
||||
UNKNOWN_PF = 0,
|
||||
/// 32-bit ARGB pixel format with alpha
|
||||
_32_ARGB8888 = 1,
|
||||
/// 32-bit RGB pixel format without alpha
|
||||
_32_RGB888 = 2,
|
||||
/// 24-bit RGB pixel format
|
||||
_24_RGB888 = 3,
|
||||
/// 16-bit RGB pixel format
|
||||
_16_RGB565 = 4,
|
||||
/// 16-bit RGB pixel format (5 bits per color)
|
||||
_16_RGB555 = 5,
|
||||
/// 16-bit ARGB pixel format (5 bits per color + 1 bit for alpha)
|
||||
_16_ARGB1555 = 6,
|
||||
/// 16-bit ARGB pixel format (4 bits per color)
|
||||
_16_ARGB4444 = 7,
|
||||
/// 8-bit RGB pixel format
|
||||
_8_RGB332 = 8,
|
||||
/// 8-bit ARGB pixel format
|
||||
_8_ARGB2222 = 9,
|
||||
/// 32-bit ABGR pixel format
|
||||
_32_ABGR8888 = 10,
|
||||
/// 32-bit RGBA pixel format
|
||||
_32_RGBA8888 = 11,
|
||||
/// 32-bit BGRA pixel format
|
||||
_32_BGRA8888 = 12,
|
||||
/// 32-bit BGR pixel format
|
||||
_32_BGR888 = 13,
|
||||
/// 24-bit BGR pixel format
|
||||
_24_BGR888 = 14,
|
||||
/// 16-bit BGR pixel format
|
||||
_16_BGR565 = 15,
|
||||
/// 16-bit BGR pixel format (5 bits per color)
|
||||
_16_BGR555 = 16,
|
||||
/// 16-bit ABGR pixel format (5 bits per color + 1 bit for alpha)
|
||||
_16_ABGR1555 = 17,
|
||||
/// 16-bit ABGR pixel format (4 bits per color)
|
||||
_16_ABGR4444 = 18,
|
||||
/// S3/DirectX Texture Compression 1
|
||||
_DXT1 = 19,
|
||||
/// S3/DirectX Texture Compression 2
|
||||
_DXT2 = 20,
|
||||
/// S3/DirectX Texture Compression 3
|
||||
_DXT3 = 21,
|
||||
/// S3/DirectX Texture Compression 4
|
||||
_DXT4 = 22,
|
||||
/// S3/DirectX Texture Compression 5
|
||||
_DXT5 = 23,
|
||||
/// 16-bit Bump Map format format (8 bits per color)
|
||||
_16_V8U8 = 24,
|
||||
/// 32-bit Bump Map format format (16 bits per color)
|
||||
_32_V16U16 = 25,
|
||||
/// 16-bit Bump Map format format with luminance
|
||||
_16_L6V5U5 = 26,
|
||||
/// 32-bit Bump Map format format with luminance
|
||||
_32_X8L8V8U8 = 27,
|
||||
/// 8 bits indexed CLUT (ABGR)
|
||||
_8_ABGR8888_CLUT = 28,
|
||||
/// 8 bits indexed CLUT (ARGB)
|
||||
_8_ARGB8888_CLUT = 29,
|
||||
/// 4 bits indexed CLUT (ABGR)
|
||||
_4_ABGR8888_CLUT = 30,
|
||||
/// 4 bits indexed CLUT (ARGB)
|
||||
_4_ARGB8888_CLUT = 31,
|
||||
}
|
||||
|
||||
/// Light type.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[repr(u32)]
|
||||
#[non_exhaustive]
|
||||
#[rustfmt::skip]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum VXLIGHT_TYPE {
|
||||
/// The Light is a point of light
|
||||
VX_LIGHTPOINT = 1,
|
||||
/// The light is a spotlight
|
||||
VX_LIGHTSPOT = 2,
|
||||
/// The light is directional light : Lights comes from an infinite point so only direction of light can be given
|
||||
VX_LIGHTDIREC = 3,
|
||||
// /// Obsolete, do not use
|
||||
// VX_LIGHTPARA = 4,
|
||||
}
|
||||
|
||||
/// Blend Mode Flags
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[repr(u32)]
|
||||
#[non_exhaustive]
|
||||
#[rustfmt::skip]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum VXTEXTURE_BLENDMODE {
|
||||
/// Texture replace any material information
|
||||
VXTEXTUREBLEND_DECAL = 1,
|
||||
/// Texture and material are combine. Alpha information of the texture replace material alpha component.
|
||||
VXTEXTUREBLEND_MODULATE = 2,
|
||||
/// Alpha information in the texture specify how material and texture are combined. Alpha information of the texture replace material alpha component.
|
||||
VXTEXTUREBLEND_DECALALPHA = 3,
|
||||
/// Alpha information in the texture specify how material and texture are combined
|
||||
VXTEXTUREBLEND_MODULATEALPHA = 4,
|
||||
VXTEXTUREBLEND_DECALMASK = 5,
|
||||
VXTEXTUREBLEND_MODULATEMASK = 6,
|
||||
/// Equivalent to DECAL
|
||||
VXTEXTUREBLEND_COPY = 7,
|
||||
VXTEXTUREBLEND_ADD = 8,
|
||||
/// Perform a Dot Product 3 between texture (normal map)and a referential vector given in VXRENDERSTATE_TEXTUREFACTOR.
|
||||
VXTEXTUREBLEND_DOTPRODUCT3 = 9,
|
||||
VXTEXTUREBLEND_MAX = 10,
|
||||
// VXTEXTUREBLEND_MASK = 0xF,
|
||||
}
|
||||
|
||||
/// Filter Mode Options
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[repr(u32)]
|
||||
#[non_exhaustive]
|
||||
#[rustfmt::skip]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum VXTEXTURE_FILTERMODE {
|
||||
/// No Filter
|
||||
VXTEXTUREFILTER_NEAREST = 1,
|
||||
/// Bilinear Interpolation
|
||||
VXTEXTUREFILTER_LINEAR = 2,
|
||||
/// Mip mapping
|
||||
VXTEXTUREFILTER_MIPNEAREST = 3,
|
||||
/// Mip Mapping with Bilinear interpolation
|
||||
VXTEXTUREFILTER_MIPLINEAR = 4,
|
||||
/// Mip Mapping with Bilinear interpolation between mipmap levels.
|
||||
VXTEXTUREFILTER_LINEARMIPNEAREST = 5,
|
||||
/// Trilinear Filtering
|
||||
VXTEXTUREFILTER_LINEARMIPLINEAR = 6,
|
||||
/// Anisotropic filtering
|
||||
VXTEXTUREFILTER_ANISOTROPIC = 7,
|
||||
// VXTEXTUREFILTER_MASK = 0xF,
|
||||
}
|
||||
|
||||
/// Texture addressing modes.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[repr(u32)]
|
||||
#[non_exhaustive]
|
||||
#[rustfmt::skip]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum VXTEXTURE_ADDRESSMODE {
|
||||
/// Default mesh wrap mode is used (see CKMesh::SetWrapMode)
|
||||
VXTEXTURE_ADDRESSWRAP = 1,
|
||||
/// Texture coordinates outside the range [0..1] are flipped evenly.
|
||||
VXTEXTURE_ADDRESSMIRROR = 2,
|
||||
/// Texture coordinates greater than 1.0 are set to 1.0, and values less than 0.0 are set to 0.0.
|
||||
VXTEXTURE_ADDRESSCLAMP = 3,
|
||||
/// When texture coordinates are greater than 1.0 or less than 0.0 texture is set to a color defined in CKMaterial::SetTextureBorderColor.
|
||||
VXTEXTURE_ADDRESSBORDER = 4,
|
||||
///
|
||||
VXTEXTURE_ADDRESSMIRRORONCE = 5,
|
||||
// /// mask for all values
|
||||
// VXTEXTURE_ADDRESSMASK = 0x7,
|
||||
}
|
||||
|
||||
/// Blending Mode options
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[repr(u32)]
|
||||
#[non_exhaustive]
|
||||
#[rustfmt::skip]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum VXBLEND_MODE {
|
||||
/// Blend factor is (0, 0, 0, 0).
|
||||
VXBLEND_ZERO = 1,
|
||||
/// Blend factor is (1, 1, 1, 1).
|
||||
VXBLEND_ONE = 2,
|
||||
/// Blend factor is (Rs, Gs, Bs, As).
|
||||
VXBLEND_SRCCOLOR = 3,
|
||||
/// Blend factor is (1-Rs, 1-Gs, 1-Bs, 1-As).
|
||||
VXBLEND_INVSRCCOLOR = 4,
|
||||
/// Blend factor is (As, As, As, As).
|
||||
VXBLEND_SRCALPHA = 5,
|
||||
/// Blend factor is (1-As, 1-As, 1-As, 1-As).
|
||||
VXBLEND_INVSRCALPHA = 6,
|
||||
/// Blend factor is (Ad, Ad, Ad, Ad).
|
||||
VXBLEND_DESTALPHA = 7,
|
||||
/// Blend factor is (1-Ad, 1-Ad, 1-Ad, 1-Ad).
|
||||
VXBLEND_INVDESTALPHA = 8,
|
||||
/// Blend factor is (Rd, Gd, Bd, Ad).
|
||||
VXBLEND_DESTCOLOR = 9,
|
||||
/// Blend factor is (1-Rd, 1-Gd, 1-Bd, 1-Ad).
|
||||
VXBLEND_INVDESTCOLOR = 10,
|
||||
/// Blend factor is (f, f, f, 1); f = min(As, 1-Ad).
|
||||
VXBLEND_SRCALPHASAT = 11,
|
||||
// /// Source blend factor is (As, As, As, As) and destination blend factor is (1-As, 1-As, 1-As, 1-As)
|
||||
// VXBLEND_BOTHSRCALPHA = 12,
|
||||
// /// Source blend factor is (1-As, 1-As, 1-As, 1-As) and destination blend factor is (As, As, As, As)
|
||||
// VXBLEND_BOTHINVSRCALPHA = 13,
|
||||
// /// Source blend factor is (1-As, 1-As, 1-As, 1-As) and destination blend factor is (As, As, As, As)
|
||||
// VXBLEND_MASK = 0xF,
|
||||
}
|
||||
|
||||
/// Fill Mode Options
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[repr(u32)]
|
||||
#[non_exhaustive]
|
||||
#[rustfmt::skip]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum VXFILL_MODE {
|
||||
/// Vertices rendering
|
||||
VXFILL_POINT = 1,
|
||||
/// Edges rendering
|
||||
VXFILL_WIREFRAME = 2,
|
||||
/// Face rendering
|
||||
VXFILL_SOLID = 3,
|
||||
// VXFILL_MASK = 3,
|
||||
}
|
||||
|
||||
/// Shade Mode Options
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[repr(u32)]
|
||||
#[non_exhaustive]
|
||||
#[rustfmt::skip]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum VXSHADE_MODE {
|
||||
/// Flat Shading
|
||||
VXSHADE_FLAT = 1,
|
||||
/// Gouraud Shading
|
||||
VXSHADE_GOURAUD = 2,
|
||||
/// Phong Shading (Not yet supported by most implementation)
|
||||
VXSHADE_PHONG = 3,
|
||||
// VXSHADE_MASK = 3,
|
||||
}
|
||||
|
||||
/// Comparison Function
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[repr(u32)]
|
||||
#[non_exhaustive]
|
||||
#[rustfmt::skip]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum VXCMPFUNC {
|
||||
/// Always fail the test.
|
||||
VXCMP_NEVER = 1,
|
||||
/// Accept if value if less than current value.
|
||||
VXCMP_LESS = 2,
|
||||
/// Accept if value if equal than current value.
|
||||
VXCMP_EQUAL = 3,
|
||||
/// Accept if value if less or equal than current value.
|
||||
VXCMP_LESSEQUAL = 4,
|
||||
/// Accept if value if greater than current value.
|
||||
VXCMP_GREATER = 5,
|
||||
/// Accept if value if different than current value.
|
||||
VXCMP_NOTEQUAL = 6,
|
||||
/// Accept if value if greater or equal current value.
|
||||
VXCMP_GREATEREQUAL = 7,
|
||||
/// Always accept the test.
|
||||
VXCMP_ALWAYS = 8,
|
||||
// /// Mask for all possible values.
|
||||
// VXCMP_MASK = 0xF,
|
||||
}
|
||||
|
||||
/// Mesh lighting options
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[repr(u32)]
|
||||
#[non_exhaustive]
|
||||
#[rustfmt::skip]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum VXMESH_LITMODE {
|
||||
/// Lighting use color information store with vertices
|
||||
VX_PRELITMESH = 0,
|
||||
/// Lighting is done by renderer using normals and face material information.
|
||||
VX_LITMESH = 1,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[repr(u32)]
|
||||
#[non_exhaustive]
|
||||
#[rustfmt::skip]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum CK_CAMERA_PROJECTION {
|
||||
CK_PERSPECTIVEPROJECTION = 1,
|
||||
CK_ORTHOGRAPHICPROJECTION = 2,
|
||||
}
|
||||
|
||||
// endregion
|
||||
288
Assets/BMapBindings/bmap-rs/tests/complete.rs
Normal file
288
Assets/BMapBindings/bmap-rs/tests/complete.rs
Normal file
@@ -0,0 +1,288 @@
|
||||
use bmap_rs::bmap_wrapper as bmap;
|
||||
use std::io::Read;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[test]
|
||||
fn test_complete() {
|
||||
// Parse arguments
|
||||
let cliopts = cli::Cli::new();
|
||||
|
||||
// Check BMap status.
|
||||
if !bmap::BMap::is_available() {
|
||||
panic!("Fail to initialize native BMap.");
|
||||
}
|
||||
|
||||
// Waiting debugger
|
||||
println!(
|
||||
"Rust PID is {}. Waiting for debugger, press any key to continue...",
|
||||
std::process::id()
|
||||
);
|
||||
let buffer = &mut [0u8];
|
||||
std::io::stdin().read_exact(buffer).unwrap();
|
||||
|
||||
// Start testbench
|
||||
let file_name = cliopts.file_name.clone();
|
||||
let temp_folder = tempfile::tempdir().unwrap();
|
||||
let mut texture_folder = cliopts.ballance_dir.clone();
|
||||
texture_folder.push("Textures");
|
||||
let encodings = cliopts.encodings.clone();
|
||||
bmap::BMap::with_bmap(|b| {
|
||||
let r = b
|
||||
.create_file_reader(
|
||||
file_name.to_str().unwrap(),
|
||||
temp_folder.path().to_str().unwrap(),
|
||||
texture_folder.to_str().unwrap(),
|
||||
encodings.iter().map(|e| e.as_str()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
testsuits::common::test(&r);
|
||||
testsuits::eq::test(&r);
|
||||
});
|
||||
drop(temp_folder);
|
||||
}
|
||||
|
||||
mod cli {
|
||||
use super::PathBuf;
|
||||
|
||||
pub struct Cli {
|
||||
/// The path to the map for loading.
|
||||
pub file_name: PathBuf,
|
||||
/// The path to the Ballance directory for finding textures
|
||||
pub ballance_dir: PathBuf,
|
||||
/// The name of encodings used by BMap for loading map.
|
||||
pub encodings: Vec<String>,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
pub fn new() -> Self {
|
||||
let file_name = std::env::var("BMAP_FILE_NAME").expect(
|
||||
"You must specify BMAP_FILE_NAME environment variable before running this test.",
|
||||
);
|
||||
let ballance_dir = std::env::var("BMAP_BALLANCE_DIR").expect(
|
||||
"You must specify BMAP_BALLANCE_DIR environment variable before running this test.",
|
||||
);
|
||||
let encodings = std::env::var("BMAP_ENCODINGS").expect(
|
||||
"You must specify BMAP_ENCODINGS environment variable before running this test.",
|
||||
);
|
||||
|
||||
Self {
|
||||
file_name: PathBuf::from(file_name),
|
||||
ballance_dir: PathBuf::from(ballance_dir),
|
||||
encodings: encodings
|
||||
.split(",")
|
||||
.map(|e| e.to_string())
|
||||
.collect::<Vec<_>>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod testsuits {
|
||||
use super::bmap;
|
||||
|
||||
pub mod common {
|
||||
use super::bmap;
|
||||
use bmap::{
|
||||
BM3dEntityDecl, BMCameraDecl, BMLightDecl, BMMaterialDecl, BMMeshDecl, BMObjectDecl,
|
||||
BMTextureDecl, BMGroupDecl
|
||||
};
|
||||
|
||||
pub fn test(reader: &bmap::BMFileReader) {
|
||||
println!("===== Groups =====");
|
||||
test_group(reader);
|
||||
println!("===== 3dObjects =====");
|
||||
test_3dobject(reader);
|
||||
println!("===== Meshes =====");
|
||||
test_mesh(reader);
|
||||
println!("===== Materials =====");
|
||||
test_material(reader);
|
||||
println!("===== Textures =====");
|
||||
test_texture(reader);
|
||||
println!("===== Target Lights =====");
|
||||
test_target_light(reader);
|
||||
println!("===== Target Cameras =====");
|
||||
test_target_camera(reader);
|
||||
println!("===== END =====");
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn test_group(reader: &bmap::BMFileReader) {
|
||||
for gp in reader.get_groups().unwrap() {
|
||||
let gp = gp.unwrap();
|
||||
|
||||
println!("{:?}", gp.get_name().unwrap());
|
||||
for gp_item in gp.get_objects().unwrap() {
|
||||
let gp_item = gp_item.unwrap();
|
||||
println!("\t{:?}", gp_item.get_name().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn test_3dobject(reader: &bmap::BMFileReader) {
|
||||
for obj in reader.get_3dobjects().unwrap() {
|
||||
let obj = obj.unwrap();
|
||||
|
||||
println!("{:?}", obj.get_name().unwrap());
|
||||
|
||||
let current_mesh = obj.get_current_mesh().unwrap();
|
||||
let mesh_name = match current_mesh {
|
||||
Some(mesh) => format!("{:?}", mesh.get_name()),
|
||||
None => "<null>".to_string(),
|
||||
};
|
||||
println!("\tMesh: {}", mesh_name);
|
||||
println!("\tVisibility: {}", obj.get_visibility().unwrap());
|
||||
println!("\tMatrix: {:?}", obj.get_world_matrix().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn test_mesh(reader: &bmap::BMFileReader) {
|
||||
for mesh in reader.get_meshes().unwrap() {
|
||||
let mesh = mesh.unwrap();
|
||||
|
||||
println!("{:?}", mesh.get_name().unwrap());
|
||||
|
||||
println!("\tLit Mode: {:?}", mesh.get_lit_mode().unwrap());
|
||||
println!("\tVertex Count: {}", mesh.get_vertex_count().unwrap());
|
||||
println!("\tFace Count: {}", mesh.get_face_count().unwrap());
|
||||
println!("\tMaterial Slot Count: {}", mesh.get_material_slot_count().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn test_material(reader: &bmap::BMFileReader) {
|
||||
for mtl in reader.get_materials().unwrap() {
|
||||
let mtl = mtl.unwrap();
|
||||
|
||||
println!("{:?}", mtl.get_name().unwrap());
|
||||
|
||||
println!("\tDiffuse: {:?}", mtl.get_diffuse().unwrap());
|
||||
println!("\tAmbient: {:?}", mtl.get_ambient().unwrap());
|
||||
println!("\tSpecular: {:?}", mtl.get_specular().unwrap());
|
||||
println!("\tEmissive: {:?}", mtl.get_emissive().unwrap());
|
||||
|
||||
println!("\tSpecular Power: {}", mtl.get_specular_power().unwrap());
|
||||
|
||||
println!("\tTexture Border Color: {:?}", mtl.get_texture_border_color().unwrap());
|
||||
|
||||
println!("\tTexture Blend Mode: {:?}", mtl.get_texture_blend_mode().unwrap());
|
||||
println!("\tTexture Min Mode: {:?}", mtl.get_texture_min_mode().unwrap());
|
||||
println!("\tTexture Mag Mode: {:?}", mtl.get_texture_mag_mode().unwrap());
|
||||
println!("\tSource Blend: {:?}", mtl.get_source_blend().unwrap());
|
||||
println!("\tDest Blend: {:?}", mtl.get_dest_blend().unwrap());
|
||||
println!("\tFill Mode: {:?}", mtl.get_fill_mode().unwrap());
|
||||
println!("\tShade Mode: {:?}", mtl.get_shade_mode().unwrap());
|
||||
|
||||
println!("\tAlpha Test Enabled: {}", mtl.get_alpha_test_enabled().unwrap());
|
||||
println!("\tAlpha Blend Enabled: {}", mtl.get_alpha_blend_enabled().unwrap());
|
||||
println!("\tPerspective Correction Enabled: {}", mtl.get_perspective_correction_enabled().unwrap());
|
||||
println!("\tZ Write Enabled: {}", mtl.get_z_write_enabled().unwrap());
|
||||
println!("\tTwo Sided Enabled: {}", mtl.get_two_sided_enabled().unwrap());
|
||||
|
||||
println!("\tAlpha Ref: {}", mtl.get_alpha_ref().unwrap());
|
||||
|
||||
println!("\tAlpha Func: {:?}", mtl.get_alpha_func().unwrap());
|
||||
println!("\tZ Func: {:?}", mtl.get_z_func().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn test_texture(reader: &bmap::BMFileReader) {
|
||||
for tex in reader.get_textures().unwrap() {
|
||||
let tex = tex.unwrap();
|
||||
|
||||
println!("{:?}", tex.get_name().unwrap());
|
||||
|
||||
println!("\tFile Name: {:?}", tex.get_file_name().unwrap());
|
||||
println!("\tSave Options: {:?}", tex.get_save_options().unwrap());
|
||||
println!("\tVideo Format: {:?}", tex.get_video_format().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn test_target_light(reader: &bmap::BMFileReader) {
|
||||
for lit in reader.get_target_lights().unwrap() {
|
||||
let lit = lit.unwrap();
|
||||
|
||||
println!("{:?}", lit.get_name().unwrap());
|
||||
|
||||
println!("\tVisibility: {:?}", lit.get_visibility().unwrap());
|
||||
println!("\tMatrix: {:?}", lit.get_world_matrix().unwrap());
|
||||
|
||||
println!("\tType: {:?}", lit.get_type().unwrap());
|
||||
println!("\tColor: {:?}", lit.get_color().unwrap());
|
||||
println!("\tConstant Attenuation: {}", lit.get_constant_attenuation().unwrap());
|
||||
println!("\tLinear Attenuation: {}", lit.get_linear_attenuation().unwrap());
|
||||
println!("\tQuadratic Attenuation: {}", lit.get_quadratic_attenuation().unwrap());
|
||||
println!("\tRange: {}", lit.get_range().unwrap());
|
||||
println!("\tHot Spot: {}", lit.get_hot_spot().unwrap());
|
||||
println!("\tFalloff: {}", lit.get_falloff().unwrap());
|
||||
println!("\tFalloff Shape: {}", lit.get_falloff_shape().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn test_target_camera(reader: &bmap::BMFileReader) {
|
||||
for cam in reader.get_target_cameras().unwrap() {
|
||||
let cam = cam.unwrap();
|
||||
|
||||
println!("{:?}", cam.get_name().unwrap());
|
||||
|
||||
println!("\tVisibility: {:?}", cam.get_visibility().unwrap());
|
||||
println!("\tMatrix: {:?}", cam.get_world_matrix().unwrap());
|
||||
|
||||
println!("\tType: {:?}", cam.get_projection_type().unwrap());
|
||||
println!("\tOrthographic Zoom: {}", cam.get_orthographic_zoom().unwrap());
|
||||
println!("\tFront Plane: {}", cam.get_front_plane().unwrap());
|
||||
println!("\tBack Plane: {}", cam.get_back_plane().unwrap());
|
||||
println!("\tFov: {}", cam.get_fov().unwrap());
|
||||
|
||||
let (width, height) = cam.get_aspect_ratio().unwrap();
|
||||
println!("\tAspect Ratio: {}:{}", width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod eq {
|
||||
use super::bmap;
|
||||
use std::collections::{BTreeSet, HashSet};
|
||||
|
||||
pub fn test(reader: &bmap::BMFileReader) {
|
||||
// Check requirements
|
||||
assert!(
|
||||
reader.get_3dobject_count().unwrap() >= 2,
|
||||
r#"Invalid file for test Eq.
|
||||
We can not perform Eq test because the length of 3dObject is too short (must greater than 2). Please choose another file to perform."#
|
||||
);
|
||||
|
||||
// Prepare variables
|
||||
let all_3dobjects = reader
|
||||
.get_3dobjects()
|
||||
.unwrap()
|
||||
.map(|o| o.unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
let first_3dobj = &all_3dobjects[0];
|
||||
let second_3dobj = &all_3dobjects[1];
|
||||
let all_3dobjects_again = reader
|
||||
.get_3dobjects()
|
||||
.unwrap()
|
||||
.map(|o| o.unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
let first_3dobj_again = &all_3dobjects_again[0];
|
||||
|
||||
// Test HashSet
|
||||
let mut test_hashset = HashSet::new();
|
||||
assert!(test_hashset.insert(first_3dobj));
|
||||
assert!(!test_hashset.insert(first_3dobj_again));
|
||||
assert!(test_hashset.insert(second_3dobj));
|
||||
|
||||
// Test BTreeSet
|
||||
let mut test_btreeset = BTreeSet::new();
|
||||
assert!(test_btreeset.insert(first_3dobj));
|
||||
assert!(!test_btreeset.insert(first_3dobj_again));
|
||||
assert!(test_btreeset.insert(second_3dobj));
|
||||
}
|
||||
}
|
||||
}
|
||||
7
Assets/BMapBindings/bmap-rs/tests/raw.rs
Normal file
7
Assets/BMapBindings/bmap-rs/tests/raw.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
use bmap_rs::bmap;
|
||||
|
||||
#[test]
|
||||
fn test_raw() {
|
||||
assert!(unsafe { bmap::BMInit() });
|
||||
assert!(unsafe { bmap::BMDispose() });
|
||||
}
|
||||
@@ -1,9 +1,19 @@
|
||||
# PyBMap
|
||||
# pybmap
|
||||
|
||||
The real scripts are placed in sub PyBMap folder. This folder is served for testbench scripts placing. You can run `testbench.py` to do a basic test for PyBMap but you may need some essential files to run this testbench which were written in `testbench.py`.
|
||||
## Layout
|
||||
|
||||
The native BMap library should be placed in sub PyBMap folder, and I have used gitignore file to filter them. The native BMap library must be named as `BMap.dll` (in Windows), `BMap.so` (in Linux or BSD), or `BMap.dylib` (in macOS). If you still can not load BMap or your system is not listed above, you should name it as `BMap.bin`.
|
||||
This project follows `src` and `test` layout and is managed by Astral UV.
|
||||
The source code of pybmap is located inside `src`.
|
||||
And the files located in `test` is used for testing.
|
||||
|
||||
Please note the most content of `virtools_types.py` are generated by EnumsMigration sub-project. Additionally the most content of `bmap.py` is generated by BMapBindings. So if some structs are updated, do not forget checking these files.
|
||||
> [!NOTE]
|
||||
> The files located in `test` are not prepared for `pytest` framework.
|
||||
> The test should be executed manually by `uv run test/main.py`.
|
||||
|
||||
Since BMap 0.3.0, testbench use command line arguments, instead of hardcode variables in code, as the arguments of BMap. It is convenient that debug BMapSharp without any modification of source code. For a brief instruction, you may need to launch BMapSharpTestbench in following command (just an example. you can modify it as you wished): `py testbench.py --file-path "LightCameraTest.nmo" --temp-dir "Temp" --texture-dir "F:/Ballance/Ballance/Textures" --encodings cp1252 gb2312`.
|
||||
## Native BMap Library Location
|
||||
|
||||
The native BMap library file should be placed with `bmap.py` file.
|
||||
|
||||
The native BMap library must be named as `BMap.dll` (in Windows), `BMap.so` (in Linux or BSD), `BMap.dylib` (in macOS), or `BMap.bin` (in any other platforms).
|
||||
|
||||
If you are building final distributed package file, such as `.whl` file, you may need manually put native BMap library file into package file because I have not write this behavior in any script.
|
||||
|
||||
@@ -3,9 +3,8 @@ name = "pybmap"
|
||||
version = "0.4.0"
|
||||
description = "The Python binding to BMap."
|
||||
readme = "README.md"
|
||||
authors = [
|
||||
{ name = "yyc12345" }
|
||||
]
|
||||
license = "MIT"
|
||||
authors = [{ name = "yyc12345" }]
|
||||
classifiers = ["Private :: Do Not Upload"]
|
||||
requires-python = ">=3.11"
|
||||
dependencies = []
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,30 +1,36 @@
|
||||
import ctypes, typing, atexit, enum
|
||||
from . import bmap, virtools_types
|
||||
import ctypes
|
||||
import typing
|
||||
import atexit
|
||||
import enum
|
||||
from . import bmap
|
||||
from . import virtools_types
|
||||
|
||||
#region Basic Class & Constant Defines
|
||||
|
||||
g_InvalidPtr: bmap.bm_void_p = bmap.bm_void_p(0)
|
||||
g_InvalidCKID: int = 0
|
||||
g_BMapEncoding: str = "utf-8"
|
||||
INVALID_PTR: bmap.bm_void_p = bmap.bm_void_p(0)
|
||||
INVALID_CKID: int = 0
|
||||
BMAP_ENCODING: str = "utf-8"
|
||||
|
||||
def _python_callback(strl: bytes):
|
||||
"""
|
||||
The Python type callback for BMFile.
|
||||
Simply add a prefix when output.
|
||||
Need a convertion before passing to BMFile.
|
||||
|
||||
This function simply add a prefix when output.
|
||||
Extra convertion is required before passing to BMFile FFI function.
|
||||
"""
|
||||
# YYC Remarks:
|
||||
# YYC MARK:
|
||||
# The passing value to this function is bytes, not bmap.bm_CKSTRING.
|
||||
# I think Python do an auto convertion in there.
|
||||
if strl is not None:
|
||||
print(f'[PyBMap] {strl.decode(g_BMapEncoding)}')
|
||||
_g_RawCallback: bmap.bm_callback = bmap.bm_callback(_python_callback)
|
||||
print(f'[pybmap] {strl.decode(BMAP_ENCODING)}')
|
||||
|
||||
RAW_CALLBACK: bmap.bm_callback = bmap.bm_callback(_python_callback)
|
||||
|
||||
#endregion
|
||||
|
||||
#region Help Functions
|
||||
|
||||
class _Utils:
|
||||
class _utils:
|
||||
@staticmethod
|
||||
def raise_out_of_length_exception() -> None:
|
||||
raise bmap.BMapException("The length of given data is too short when assigning struct array.")
|
||||
@@ -39,7 +45,7 @@ class _Utils:
|
||||
pdata[idx] = user_vector[_j]
|
||||
idx += 1
|
||||
except StopIteration:
|
||||
_Utils.raise_out_of_length_exception()
|
||||
_utils.raise_out_of_length_exception()
|
||||
|
||||
@staticmethod
|
||||
def _vector_iterator(pdata: typing.Any, item_count: int, factor_count: int) -> typing.Iterator[tuple[typing.Any, ...]]:
|
||||
@@ -53,39 +59,37 @@ class _Utils:
|
||||
|
||||
@staticmethod
|
||||
def vxvector3_assigner(pvector: bmap.bm_VxVector3_p, count: int, itor: typing.Iterator[virtools_types.VxVector3]) -> None:
|
||||
_Utils._vector_assigner(ctypes.cast(pvector, bmap.bm_CKFLOAT_p), count, 3, map(lambda v: (v.x, v.y, v.z), itor))
|
||||
_utils._vector_assigner(ctypes.cast(pvector, bmap.bm_CKFLOAT_p), count, 3, map(lambda v: (v.x, v.y, v.z), itor))
|
||||
@staticmethod
|
||||
def vxvector3_iterator(pvector: bmap.bm_VxVector3_p, count: int) -> typing.Iterator[virtools_types.VxVector3]:
|
||||
return map(
|
||||
lambda v: virtools_types.VxVector3(*v),
|
||||
_Utils._vector_iterator(ctypes.cast(pvector, bmap.bm_CKFLOAT_p), count, 3)
|
||||
_utils._vector_iterator(ctypes.cast(pvector, bmap.bm_CKFLOAT_p), count, 3)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def vxvector2_assigner(pvector: bmap.bm_VxVector2_p, count: int, itor: typing.Iterator[virtools_types.VxVector2]) -> None:
|
||||
_Utils._vector_assigner(ctypes.cast(pvector, bmap.bm_CKFLOAT_p), count, 2, map(lambda v: (v.x, v.y), itor))
|
||||
_utils._vector_assigner(ctypes.cast(pvector, bmap.bm_CKFLOAT_p), count, 2, map(lambda v: (v.x, v.y), itor))
|
||||
@staticmethod
|
||||
def vxvector2_iterator(pvector: bmap.bm_VxVector2_p, count: int) -> typing.Iterator[virtools_types.VxVector2]:
|
||||
return map(
|
||||
lambda v: virtools_types.VxVector2(*v),
|
||||
_Utils._vector_iterator(ctypes.cast(pvector, bmap.bm_CKFLOAT_p), count, 2)
|
||||
_utils._vector_iterator(ctypes.cast(pvector, bmap.bm_CKFLOAT_p), count, 2)
|
||||
)
|
||||
|
||||
"""!
|
||||
@remarks
|
||||
bmap.bm_CKWORD_p | bmap.bm_CKDWORD_p is just a type hint.
|
||||
We actually do not need distinguish them in code.
|
||||
Because the stride when increasing them is decided by their runtime type.
|
||||
"""
|
||||
# YYC MARK:
|
||||
# bmap.bm_CKWORD_p | bmap.bm_CKDWORD_p is just a type hint.
|
||||
# We actually do not need distinguish them in code.
|
||||
# Because the stride when increasing them is decided by their runtime type.
|
||||
|
||||
@staticmethod
|
||||
def ckfaceindices_assigner(pindices: bmap.bm_CKWORD_p | bmap.bm_CKDWORD_p, count: int, itor: typing.Iterator[virtools_types.CKFaceIndices]) -> None:
|
||||
_Utils._vector_assigner(pindices, count, 3, map(lambda v: (v.i1, v.i2, v.i3), itor))
|
||||
_utils._vector_assigner(pindices, count, 3, map(lambda v: (v.i1, v.i2, v.i3), itor))
|
||||
@staticmethod
|
||||
def ckfaceindices_iterator(pindices: bmap.bm_CKWORD_p | bmap.bm_CKDWORD_p, count: int) -> typing.Iterator[virtools_types.CKFaceIndices]:
|
||||
return map(
|
||||
lambda v: virtools_types.CKFaceIndices(*v),
|
||||
_Utils._vector_iterator(pindices, count, 3)
|
||||
_utils._vector_iterator(pindices, count, 3)
|
||||
)
|
||||
|
||||
#endregion
|
||||
@@ -122,7 +126,7 @@ class _AbstractPointer():
|
||||
TEnumType = typing.TypeVar('TEnumType', bound = enum.IntEnum)
|
||||
TIntegralType = bmap.bm_CKDWORD | bmap.bm_CKWORD | bmap.bm_CKINT | bmap.bm_CKBYTE | bmap.bm_CKID
|
||||
TFloatPointType = bmap.bm_CKFLOAT
|
||||
TPointerType = typing.TypeVar('TPointerType')
|
||||
TPointerType = typing.TypeVar('TPointerType', bound=ctypes._Pointer)
|
||||
|
||||
class _AbstractCKObject(_AbstractPointer):
|
||||
__mCKID: int
|
||||
@@ -148,6 +152,7 @@ class _AbstractCKObject(_AbstractPointer):
|
||||
def __hash__(self) -> int:
|
||||
return hash((_AbstractPointer.__hash__(self), self.__mCKID))
|
||||
|
||||
# YYC MARK:
|
||||
# Convenient Value Getter Setter
|
||||
# Focusing on those which widely called types.
|
||||
|
||||
@@ -183,11 +188,11 @@ class _AbstractCKObject(_AbstractPointer):
|
||||
data: bmap.bm_CKSTRING = bmap.bm_CKSTRING()
|
||||
getter_(self._get_pointer(), self._get_ckid(), ctypes.byref(data))
|
||||
if data.value is None: return None
|
||||
else: return data.value.decode(g_BMapEncoding)
|
||||
else: return data.value.decode(BMAP_ENCODING)
|
||||
def _set_str_value(self, setter_: typing.Callable[[bmap.bm_void_p, bmap.bm_CKID, bmap.bm_CKSTRING], bmap.bm_bool], data_: str | None) -> None:
|
||||
data: bmap.bm_CKSTRING
|
||||
if data_ is None: data = bmap.bm_CKSTRING(0)
|
||||
else: data = bmap.bm_CKSTRING(data_.encode(g_BMapEncoding))
|
||||
else: data = bmap.bm_CKSTRING(data_.encode(BMAP_ENCODING))
|
||||
setter_(self._get_pointer(), self._get_ckid(), data)
|
||||
|
||||
def _set_vxcolor_value(self,
|
||||
@@ -209,7 +214,7 @@ class _AbstractCKObject(_AbstractPointer):
|
||||
return ret
|
||||
|
||||
def _get_pointer_value(self, ptr_type_: type[TPointerType], getter_: typing.Callable[[bmap.bm_void_p, bmap.bm_CKID, typing.Any], bmap.bm_bool]) -> TPointerType:
|
||||
data = ptr_type_()
|
||||
data: TPointerType = ptr_type_()
|
||||
getter_(self._get_pointer(), self._get_ckid(), ctypes.byref(data))
|
||||
return data
|
||||
|
||||
@@ -234,18 +239,16 @@ if is_bmap_available():
|
||||
|
||||
#region Real Type Defines
|
||||
|
||||
"""!
|
||||
@remarks
|
||||
BMFileReader, BMFileWriter, and BMMeshTrans can be create by given constructor.
|
||||
But they must be destroyed by calling dispose(). Otherwise it may cause memory leak.
|
||||
You also can use python `with` statement to achieve this automatically.
|
||||
|
||||
BMObject, BMTexture, BMMaterial, BMMesh, and BM3dObject should NOT be constructed from given constructor.
|
||||
They must be obtained from BMFileReader, BMFileWriter, and BMMeshTrans.
|
||||
Thus BMObject, BMTexture, BMMaterial, BMMesh, and BM3dObject also do not need to free
|
||||
because these resources are sotred in BMFileReader, BMFileWriter, and BMMeshTrans.
|
||||
We just provide them as a visitor.
|
||||
"""
|
||||
# YYC MARK:
|
||||
# BMFileReader, BMFileWriter, and BMMeshTrans can be create by given constructor.
|
||||
# But they must be destroyed by calling dispose(). Otherwise it may cause memory leak.
|
||||
# You also can use python `with` statement to achieve this automatically.
|
||||
#
|
||||
# BMObject, BMTexture, BMMaterial, BMMesh, and BM3dObject should NOT be constructed from given constructor.
|
||||
# They must be obtained from BMFileReader, BMFileWriter, and BMMeshTrans.
|
||||
# Thus BMObject, BMTexture, BMMaterial, BMMesh, and BM3dObject also do not need to free
|
||||
# because these resources are sotred in BMFileReader, BMFileWriter, and BMMeshTrans.
|
||||
# We just provide them as a visitor.
|
||||
|
||||
class BMObject(_AbstractCKObject):
|
||||
def get_name(self) -> str | None:
|
||||
@@ -258,10 +261,10 @@ class BMTexture(BMObject):
|
||||
return self._get_str_value(bmap.BMTexture_GetFileName)
|
||||
|
||||
def load_image(self, filepath: str) -> None:
|
||||
filename: bmap.bm_CKSTRING = bmap.bm_CKSTRING(filepath.encode(g_BMapEncoding))
|
||||
filename: bmap.bm_CKSTRING = bmap.bm_CKSTRING(filepath.encode(BMAP_ENCODING))
|
||||
bmap.BMTexture_LoadImage(self._get_pointer(), self._get_ckid(), filename)
|
||||
def save_image(self, filepath: str) -> None:
|
||||
filename: bmap.bm_CKSTRING = bmap.bm_CKSTRING(filepath.encode(g_BMapEncoding))
|
||||
filename: bmap.bm_CKSTRING = bmap.bm_CKSTRING(filepath.encode(BMAP_ENCODING))
|
||||
bmap.BMTexture_SaveImage(self._get_pointer(), self._get_ckid(), filename)
|
||||
|
||||
def get_save_options(self) -> virtools_types.CK_TEXTURE_SAVEOPTIONS:
|
||||
@@ -299,11 +302,11 @@ class BMMaterial(BMObject):
|
||||
def get_texture(self) -> BMTexture | None:
|
||||
objid: bmap.bm_CKID = bmap.bm_CKID()
|
||||
bmap.BMMaterial_GetTexture(self._get_pointer(), self._get_ckid(), ctypes.byref(objid))
|
||||
if objid.value == g_InvalidCKID: return None
|
||||
if objid.value == INVALID_CKID: return None
|
||||
else: return BMTexture(self._get_pointer(), objid)
|
||||
|
||||
def set_texture(self, tex_: BMTexture | None) -> None:
|
||||
objid: bmap.bm_CKID = bmap.bm_CKID(g_InvalidCKID)
|
||||
objid: bmap.bm_CKID = bmap.bm_CKID(INVALID_CKID)
|
||||
if tex_ is not None: objid = tex_._get_ckid()
|
||||
bmap.BMMaterial_SetTexture(self._get_pointer(), self._get_ckid(), objid)
|
||||
|
||||
@@ -398,25 +401,25 @@ class BMMesh(BMObject):
|
||||
def get_vertex_positions(self) -> typing.Iterator[virtools_types.VxVector3]:
|
||||
# get raw pointer and return
|
||||
raw_vector = self._get_pointer_value(bmap.bm_VxVector3_p, bmap.BMMesh_GetVertexPositions)
|
||||
return _Utils.vxvector3_iterator(raw_vector, self.get_vertex_count())
|
||||
return _utils.vxvector3_iterator(raw_vector, self.get_vertex_count())
|
||||
def set_vertex_positions(self, itor: typing.Iterator[virtools_types.VxVector3]) -> None:
|
||||
# get raw float pointer and assign
|
||||
raw_vector = self._get_pointer_value(bmap.bm_VxVector3_p, bmap.BMMesh_GetVertexPositions)
|
||||
_Utils.vxvector3_assigner(raw_vector, self.get_vertex_count(), itor)
|
||||
_utils.vxvector3_assigner(raw_vector, self.get_vertex_count(), itor)
|
||||
|
||||
def get_vertex_normals(self) -> typing.Iterator[virtools_types.VxVector3]:
|
||||
raw_vector = self._get_pointer_value(bmap.bm_VxVector3_p, bmap.BMMesh_GetVertexNormals)
|
||||
return _Utils.vxvector3_iterator(raw_vector, self.get_vertex_count())
|
||||
return _utils.vxvector3_iterator(raw_vector, self.get_vertex_count())
|
||||
def set_vertex_normals(self, itor: typing.Iterator[virtools_types.VxVector3]) -> None:
|
||||
raw_vector = self._get_pointer_value(bmap.bm_VxVector3_p, bmap.BMMesh_GetVertexNormals)
|
||||
_Utils.vxvector3_assigner(raw_vector, self.get_vertex_count(), itor)
|
||||
_utils.vxvector3_assigner(raw_vector, self.get_vertex_count(), itor)
|
||||
|
||||
def get_vertex_uvs(self) -> typing.Iterator[virtools_types.VxVector2]:
|
||||
raw_vector = self._get_pointer_value(bmap.bm_VxVector2_p, bmap.BMMesh_GetVertexUVs)
|
||||
return _Utils.vxvector2_iterator(raw_vector, self.get_vertex_count())
|
||||
return _utils.vxvector2_iterator(raw_vector, self.get_vertex_count())
|
||||
def set_vertex_uvs(self, itor: typing.Iterator[virtools_types.VxVector2]) -> None:
|
||||
raw_vector = self._get_pointer_value(bmap.bm_VxVector2_p, bmap.BMMesh_GetVertexUVs)
|
||||
_Utils.vxvector2_assigner(raw_vector, self.get_vertex_count(), itor)
|
||||
_utils.vxvector2_assigner(raw_vector, self.get_vertex_count(), itor)
|
||||
|
||||
def get_face_count(self) -> int:
|
||||
return self._get_integral_value(bmap.bm_CKDWORD, bmap.BMMesh_GetFaceCount)
|
||||
@@ -425,10 +428,10 @@ class BMMesh(BMObject):
|
||||
|
||||
def get_face_indices(self) -> typing.Iterator[virtools_types.CKFaceIndices]:
|
||||
raw_idx = self._get_pointer_value(bmap.bm_CKWORD_p, bmap.BMMesh_GetFaceIndices)
|
||||
return _Utils.ckfaceindices_iterator(raw_idx, self.get_face_count())
|
||||
return _utils.ckfaceindices_iterator(raw_idx, self.get_face_count())
|
||||
def set_face_indices(self, itor: typing.Iterator[virtools_types.CKFaceIndices]) -> None:
|
||||
raw_idx = self._get_pointer_value(bmap.bm_CKWORD_p, bmap.BMMesh_GetFaceIndices)
|
||||
_Utils.ckfaceindices_assigner(raw_idx, self.get_face_count(), itor)
|
||||
_utils.ckfaceindices_assigner(raw_idx, self.get_face_count(), itor)
|
||||
|
||||
def get_face_material_slot_indexs(self) -> typing.Iterator[int]:
|
||||
raw_idx = self._get_pointer_value(bmap.bm_CKWORD_p, bmap.BMMesh_GetFaceMaterialSlotIndexs)
|
||||
@@ -441,7 +444,7 @@ class BMMesh(BMObject):
|
||||
for i in range(self.get_face_count()):
|
||||
raw_idx[i] = next(itor)
|
||||
except StopIteration:
|
||||
_Utils.raise_out_of_length_exception()
|
||||
_utils.raise_out_of_length_exception()
|
||||
|
||||
def get_material_slot_count(self) -> int:
|
||||
return self._get_integral_value(bmap.bm_CKDWORD, bmap.BMMesh_GetMaterialSlotCount)
|
||||
@@ -454,7 +457,7 @@ class BMMesh(BMObject):
|
||||
for i in range(self.get_material_slot_count()):
|
||||
idx.value = i
|
||||
bmap.BMMesh_GetMaterialSlot(self._get_pointer(), self._get_ckid(), idx, ctypes.byref(mtlid))
|
||||
if mtlid.value == g_InvalidCKID:
|
||||
if mtlid.value == INVALID_CKID:
|
||||
yield None
|
||||
else:
|
||||
yield BMMaterial(self._get_pointer(), mtlid)
|
||||
@@ -468,13 +471,13 @@ class BMMesh(BMObject):
|
||||
# analyze mtl item
|
||||
mtlobj: BMMaterial | None = next(itor)
|
||||
if mtlobj is None:
|
||||
mtlid.value = g_InvalidCKID
|
||||
mtlid.value = INVALID_CKID
|
||||
else:
|
||||
mtlid = mtlobj._get_ckid()
|
||||
# set
|
||||
bmap.BMMesh_SetMaterialSlot(self._get_pointer(), self._get_ckid(), idx, mtlid)
|
||||
except StopIteration:
|
||||
_Utils.raise_out_of_length_exception()
|
||||
_utils.raise_out_of_length_exception()
|
||||
|
||||
class BM3dEntity(BMObject):
|
||||
def get_world_matrix(self) -> virtools_types.VxMatrix:
|
||||
@@ -494,13 +497,13 @@ class BM3dEntity(BMObject):
|
||||
def get_current_mesh(self) -> BMMesh | None:
|
||||
ckid: bmap.bm_CKID = bmap.bm_CKID()
|
||||
bmap.BM3dEntity_GetCurrentMesh(self._get_pointer(), self._get_ckid(), ctypes.byref(ckid))
|
||||
if ckid.value == g_InvalidCKID:
|
||||
if ckid.value == INVALID_CKID:
|
||||
return None
|
||||
else:
|
||||
return BMMesh(self._get_pointer(), ckid)
|
||||
|
||||
def set_current_mesh(self, mesh: BMMesh | None) -> None:
|
||||
ckid: bmap.bm_CKID = bmap.bm_CKID(g_InvalidCKID)
|
||||
ckid: bmap.bm_CKID = bmap.bm_CKID(INVALID_CKID)
|
||||
if mesh is not None:
|
||||
ckid = mesh._get_ckid()
|
||||
bmap.BM3dEntity_SetCurrentMesh(self._get_pointer(), self._get_ckid(), ckid)
|
||||
@@ -558,6 +561,43 @@ class BMLight(BM3dEntity):
|
||||
class BMTargetLight(BMLight):
|
||||
pass
|
||||
|
||||
class BMCamera(BM3dEntity):
|
||||
def get_projection_type(self) -> virtools_types.CK_CAMERA_PROJECTION:
|
||||
return self._get_enum_value(virtools_types.CK_CAMERA_PROJECTION, bmap.BMCamera_GetProjectionType)
|
||||
def set_projection_type(self, data_: virtools_types.CK_CAMERA_PROJECTION) -> None:
|
||||
self._set_enum_value(bmap.BMCamera_SetProjectionType, data_)
|
||||
|
||||
def get_orthographic_zoom(self) -> float:
|
||||
return self._get_float_point_value(bmap.bm_CKFLOAT, bmap.BMCamera_GetOrthographicZoom)
|
||||
def set_orthographic_zoom(self, val_: float) -> None:
|
||||
self._set_float_point_value(bmap.bm_CKFLOAT, bmap.BMCamera_SetOrthographicZoom, val_)
|
||||
|
||||
def get_front_plane(self) -> float:
|
||||
return self._get_float_point_value(bmap.bm_CKFLOAT, bmap.BMCamera_GetFrontPlane)
|
||||
def set_front_plane(self, val_: float) -> None:
|
||||
self._set_float_point_value(bmap.bm_CKFLOAT, bmap.BMCamera_SetFrontPlane, val_)
|
||||
def get_back_plane(self) -> float:
|
||||
return self._get_float_point_value(bmap.bm_CKFLOAT, bmap.BMCamera_GetBackPlane)
|
||||
def set_back_plane(self, val_: float) -> None:
|
||||
self._set_float_point_value(bmap.bm_CKFLOAT, bmap.BMCamera_SetBackPlane, val_)
|
||||
def get_fov(self) -> float:
|
||||
return self._get_float_point_value(bmap.bm_CKFLOAT, bmap.BMCamera_GetFov)
|
||||
def set_fov(self, val_: float) -> None:
|
||||
self._set_float_point_value(bmap.bm_CKFLOAT, bmap.BMCamera_SetFov, val_)
|
||||
|
||||
def get_aspect_ratio(self) -> tuple[int, int]:
|
||||
width = bmap.bm_CKDWORD()
|
||||
height = bmap.bm_CKDWORD()
|
||||
bmap.BMCamera_GetAspectRatio(self._get_pointer(), self._get_ckid(), ctypes.byref(width), ctypes.byref(height))
|
||||
return (width.value, height.value)
|
||||
def set_aspect_ratio(self, width_: int, height_: int) -> None:
|
||||
width = bmap.bm_CKDWORD(width_)
|
||||
height = bmap.bm_CKDWORD(height_)
|
||||
bmap.BMCamera_SetAspectRatio(self._get_pointer(), self._get_ckid(), width, height)
|
||||
|
||||
class BMTargetCamera(BMCamera):
|
||||
pass
|
||||
|
||||
class BMGroup(BMObject):
|
||||
def add_object(self, member: BM3dObject) -> None:
|
||||
bmap.BMGroup_AddObject(self._get_pointer(), self._get_ckid(), member._get_ckid())
|
||||
@@ -578,19 +618,19 @@ class BMGroup(BMObject):
|
||||
yield BM3dObject(self._get_pointer(), retid)
|
||||
|
||||
class BMFileReader(_AbstractPointer):
|
||||
def __init__(self, file_name_: str, temp_folder_: str, texture_folder_: str, encodings_: tuple[str]):
|
||||
def __init__(self, file_name_: str, temp_folder_: str, texture_folder_: str, encodings_: tuple[str, ...]):
|
||||
# create param
|
||||
file_name: bmap.bm_CKSTRING = bmap.bm_CKSTRING(file_name_.encode(g_BMapEncoding))
|
||||
temp_folder: bmap.bm_CKSTRING = bmap.bm_CKSTRING(temp_folder_.encode(g_BMapEncoding))
|
||||
texture_folder: bmap.bm_CKSTRING = bmap.bm_CKSTRING(texture_folder_.encode(g_BMapEncoding))
|
||||
file_name: bmap.bm_CKSTRING = bmap.bm_CKSTRING(file_name_.encode(BMAP_ENCODING))
|
||||
temp_folder: bmap.bm_CKSTRING = bmap.bm_CKSTRING(temp_folder_.encode(BMAP_ENCODING))
|
||||
texture_folder: bmap.bm_CKSTRING = bmap.bm_CKSTRING(texture_folder_.encode(BMAP_ENCODING))
|
||||
encoding_count: bmap.bm_CKDWORD = bmap.bm_CKDWORD(len(encodings_))
|
||||
encodings: ctypes.Array = (bmap.bm_CKSTRING * len(encodings_))(
|
||||
*(strl.encode(g_BMapEncoding) for strl in encodings_)
|
||||
*(strl.encode(BMAP_ENCODING) for strl in encodings_)
|
||||
)
|
||||
out_file: bmap.bm_void_p = bmap.bm_void_p()
|
||||
# exec
|
||||
bmap.BMFile_Load(
|
||||
file_name, temp_folder, texture_folder, _g_RawCallback,
|
||||
file_name, temp_folder, texture_folder, RAW_CALLBACK,
|
||||
encoding_count, encodings,
|
||||
ctypes.byref(out_file)
|
||||
)
|
||||
@@ -606,7 +646,7 @@ class BMFileReader(_AbstractPointer):
|
||||
def dispose(self) -> None:
|
||||
if self._is_valid():
|
||||
bmap.BMFile_Free(self._get_pointer())
|
||||
self._set_pointer(g_InvalidPtr)
|
||||
self._set_pointer(INVALID_PTR)
|
||||
|
||||
def __get_ckobject_count(self,
|
||||
count_getter: typing.Callable[[bmap.bm_void_p, bmap.bm_CKDWORD_p], bmap.bm_bool]) -> int:
|
||||
@@ -655,20 +695,25 @@ class BMFileReader(_AbstractPointer):
|
||||
return self.__get_ckobject_count(bmap.BMFile_GetTargetLightCount)
|
||||
def get_target_lights(self) -> typing.Iterator[BMTargetLight]:
|
||||
return self.__get_ckobjects(BMTargetLight, bmap.BMFile_GetTargetLightCount, bmap.BMFile_GetTargetLight)
|
||||
def get_target_camera_count(self) -> int:
|
||||
return self.__get_ckobject_count(bmap.BMFile_GetTargetCameraCount)
|
||||
def get_target_cameras(self) -> typing.Iterator[BMTargetCamera]:
|
||||
return self.__get_ckobjects(BMTargetCamera, bmap.BMFile_GetTargetCameraCount, bmap.BMFile_GetTargetCamera)
|
||||
|
||||
|
||||
class BMFileWriter(_AbstractPointer):
|
||||
def __init__(self, temp_folder_: str, texture_folder_: str, encodings_: tuple[str]):
|
||||
def __init__(self, temp_folder_: str, texture_folder_: str, encodings_: tuple[str, ...]):
|
||||
# create param
|
||||
temp_folder: bmap.bm_CKSTRING = bmap.bm_CKSTRING(temp_folder_.encode(g_BMapEncoding))
|
||||
texture_folder: bmap.bm_CKSTRING = bmap.bm_CKSTRING(texture_folder_.encode(g_BMapEncoding))
|
||||
temp_folder: bmap.bm_CKSTRING = bmap.bm_CKSTRING(temp_folder_.encode(BMAP_ENCODING))
|
||||
texture_folder: bmap.bm_CKSTRING = bmap.bm_CKSTRING(texture_folder_.encode(BMAP_ENCODING))
|
||||
encoding_count: bmap.bm_CKDWORD = bmap.bm_CKDWORD(len(encodings_))
|
||||
encodings: ctypes.Array = (bmap.bm_CKSTRING * len(encodings_))(
|
||||
*(strl.encode(g_BMapEncoding) for strl in encodings_)
|
||||
*(strl.encode(BMAP_ENCODING) for strl in encodings_)
|
||||
)
|
||||
out_file: bmap.bm_void_p = bmap.bm_void_p()
|
||||
# exec
|
||||
bmap.BMFile_Create(
|
||||
temp_folder, texture_folder, _g_RawCallback,
|
||||
temp_folder, texture_folder, RAW_CALLBACK,
|
||||
encoding_count, encodings,
|
||||
ctypes.byref(out_file)
|
||||
)
|
||||
@@ -683,7 +728,7 @@ class BMFileWriter(_AbstractPointer):
|
||||
|
||||
def save(self, file_name_: str, texture_save_opt_: virtools_types.CK_TEXTURE_SAVEOPTIONS, use_compress_: bool, compress_level_: int) -> None:
|
||||
# create param
|
||||
file_name: bmap.bm_CKSTRING = bmap.bm_CKSTRING(file_name_.encode(g_BMapEncoding))
|
||||
file_name: bmap.bm_CKSTRING = bmap.bm_CKSTRING(file_name_.encode(BMAP_ENCODING))
|
||||
texture_save_opt: bmap.bm_enum = bmap.bm_enum(texture_save_opt_.value)
|
||||
use_compress: bmap.bm_bool = bmap.bm_bool(use_compress_)
|
||||
compress_level: bmap.bm_CKINT = bmap.bm_CKINT(compress_level_)
|
||||
@@ -693,7 +738,7 @@ class BMFileWriter(_AbstractPointer):
|
||||
def dispose(self) -> None:
|
||||
if self._is_valid():
|
||||
bmap.BMFile_Free(self._get_pointer())
|
||||
self._set_pointer(g_InvalidPtr)
|
||||
self._set_pointer(INVALID_PTR)
|
||||
|
||||
def __create_ckobject(self,
|
||||
class_type: type[TCKObject],
|
||||
@@ -717,6 +762,8 @@ class BMFileWriter(_AbstractPointer):
|
||||
return self.__create_ckobject(BMGroup, bmap.BMFile_CreateGroup)
|
||||
def create_target_light(self) -> BMTargetLight:
|
||||
return self.__create_ckobject(BMTargetLight, bmap.BMFile_CreateTargetLight)
|
||||
def create_target_camera(self) -> BMTargetCamera:
|
||||
return self.__create_ckobject(BMTargetCamera, bmap.BMFile_CreateTargetCamera)
|
||||
|
||||
class BMMeshTrans(_AbstractPointer):
|
||||
def __init__(self):
|
||||
@@ -733,7 +780,7 @@ class BMMeshTrans(_AbstractPointer):
|
||||
def dispose(self) -> None:
|
||||
if self._is_valid():
|
||||
bmap.BMMeshTrans_Delete(self._get_pointer())
|
||||
self._set_pointer(g_InvalidPtr)
|
||||
self._set_pointer(INVALID_PTR)
|
||||
|
||||
def parse(self, objmesh: BMMesh) -> None:
|
||||
bmap.BMMeshTrans_Parse(self._get_pointer(), objmesh._get_pointer(), objmesh._get_ckid())
|
||||
@@ -746,7 +793,7 @@ class BMMeshTrans(_AbstractPointer):
|
||||
raw_vector: bmap.bm_VxVector3_p = bmap.bm_VxVector3_p()
|
||||
bmap.BMMeshTrans_PrepareVertex(self._get_pointer(), ctypes.byref(raw_vector))
|
||||
# set by pointer
|
||||
_Utils.vxvector3_assigner(raw_vector, count, itor)
|
||||
_utils.vxvector3_assigner(raw_vector, count, itor)
|
||||
|
||||
def prepare_normal(self, count: int, itor: typing.Iterator[virtools_types.VxVector3]) -> None:
|
||||
csize: bmap.bm_CKDWORD = bmap.bm_CKDWORD(count)
|
||||
@@ -755,7 +802,7 @@ class BMMeshTrans(_AbstractPointer):
|
||||
raw_vector: bmap.bm_VxVector3_p = bmap.bm_VxVector3_p()
|
||||
bmap.BMMeshTrans_PrepareNormal(self._get_pointer(), ctypes.byref(raw_vector))
|
||||
|
||||
_Utils.vxvector3_assigner(raw_vector, count, itor)
|
||||
_utils.vxvector3_assigner(raw_vector, count, itor)
|
||||
|
||||
def prepare_uv(self, count: int, itor: typing.Iterator[virtools_types.VxVector2]) -> None:
|
||||
csize: bmap.bm_CKDWORD = bmap.bm_CKDWORD(count)
|
||||
@@ -764,7 +811,7 @@ class BMMeshTrans(_AbstractPointer):
|
||||
raw_vector: bmap.bm_VxVector2_p = bmap.bm_VxVector2_p()
|
||||
bmap.BMMeshTrans_PrepareUV(self._get_pointer(), ctypes.byref(raw_vector))
|
||||
|
||||
_Utils.vxvector2_assigner(raw_vector, count, itor)
|
||||
_utils.vxvector2_assigner(raw_vector, count, itor)
|
||||
|
||||
def prepare_mtl_slot(self, count: int, itor: typing.Iterator[BMMaterial | None]) -> None:
|
||||
csize: bmap.bm_CKDWORD = bmap.bm_CKDWORD(count)
|
||||
@@ -777,12 +824,12 @@ class BMMeshTrans(_AbstractPointer):
|
||||
for _ in range(count):
|
||||
usermtl: BMMaterial | None = next(itor)
|
||||
if usermtl is None:
|
||||
raw_ckid[idx] = g_InvalidCKID
|
||||
raw_ckid[idx] = INVALID_CKID
|
||||
else:
|
||||
raw_ckid[idx] = usermtl._get_ckid().value
|
||||
idx += 1
|
||||
except StopIteration:
|
||||
_Utils.raise_out_of_length_exception()
|
||||
_utils.raise_out_of_length_exception()
|
||||
|
||||
def prepare_face(self,
|
||||
count: int,
|
||||
@@ -806,9 +853,9 @@ class BMMeshTrans(_AbstractPointer):
|
||||
|
||||
# iterate and assign
|
||||
# assigne triple indices
|
||||
_Utils.ckfaceindices_assigner(raw_vec_idx, count, vec_idx)
|
||||
_Utils.ckfaceindices_assigner(raw_nml_idx, count, nml_idx)
|
||||
_Utils.ckfaceindices_assigner(raw_uv_idx, count, uv_idx)
|
||||
_utils.ckfaceindices_assigner(raw_vec_idx, count, vec_idx)
|
||||
_utils.ckfaceindices_assigner(raw_nml_idx, count, nml_idx)
|
||||
_utils.ckfaceindices_assigner(raw_uv_idx, count, uv_idx)
|
||||
# assign mtl index
|
||||
try:
|
||||
idx: int = 0
|
||||
@@ -816,6 +863,6 @@ class BMMeshTrans(_AbstractPointer):
|
||||
raw_mtl_idx[idx] = next(mtl_idx)
|
||||
idx += 1
|
||||
except StopIteration:
|
||||
_Utils.raise_out_of_length_exception()
|
||||
_utils.raise_out_of_length_exception()
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import typing, enum
|
||||
import enum
|
||||
|
||||
ConstVxVector2 = tuple[float, float]
|
||||
ConstVxVector3 = tuple[float, float, float]
|
||||
@@ -175,153 +175,250 @@ class VxMatrix():
|
||||
)
|
||||
|
||||
class CK_TEXTURE_SAVEOPTIONS(enum.IntEnum):
|
||||
"""!
|
||||
"""
|
||||
Specify the way textures or sprites will be saved
|
||||
"""
|
||||
CKTEXTURE_RAWDATA = 0 ##< Save raw data inside file. The bitmap is saved in a raw 32 bit per pixel format.
|
||||
CKTEXTURE_EXTERNAL = 1 ##< Store only the file name for the texture. The bitmap file must be present in the bitmap paths when loading the composition.
|
||||
CKTEXTURE_IMAGEFORMAT = 2 ##< Save using format specified. The bitmap data will be converted to the specified format by the correspondant bitmap plugin and saved inside file.
|
||||
CKTEXTURE_USEGLOBAL = 3 ##< Use Global settings, that is the settings given with CKContext::SetGlobalImagesSaveOptions. (Not valid when using CKContext::SetImagesSaveOptions).
|
||||
CKTEXTURE_INCLUDEORIGINALFILE = 4 ##< Insert original image file inside CMO file. The bitmap file that was used originally for the texture or sprite will be append to the composition file and extracted when the file is loaded.
|
||||
CKTEXTURE_RAWDATA = 0
|
||||
"""Save raw data inside file. The bitmap is saved in a raw 32 bit per pixel format."""
|
||||
CKTEXTURE_EXTERNAL = 1
|
||||
"""Store only the file name for the texture. The bitmap file must be present in the bitmap pathswhen loading the composition."""
|
||||
CKTEXTURE_IMAGEFORMAT = 2
|
||||
"""Save using format specified. The bitmap data will be converted to thespecified format by the correspondant bitmap plugin and saved inside file."""
|
||||
CKTEXTURE_USEGLOBAL = 3
|
||||
"""Use Global settings, that is the settings given with CKContext::SetGlobalImagesSaveOptions. (Not valid when using CKContext::SetImagesSaveOptions)."""
|
||||
CKTEXTURE_INCLUDEORIGINALFILE = 4
|
||||
"""Insert original image file inside CMO file. The bitmap file thatwas used originally for the texture or sprite will be append tothe composition file and extracted when the file is loaded."""
|
||||
|
||||
class VX_PIXELFORMAT(enum.IntEnum):
|
||||
"""!
|
||||
"""
|
||||
Pixel format types.
|
||||
"""
|
||||
#UNKNOWN_PF = 0 ##< Unknown pixel format
|
||||
_32_ARGB8888 = 1 ##< 32-bit ARGB pixel format with alpha
|
||||
_32_RGB888 = 2 ##< 32-bit RGB pixel format without alpha
|
||||
_24_RGB888 = 3 ##< 24-bit RGB pixel format
|
||||
_16_RGB565 = 4 ##< 16-bit RGB pixel format
|
||||
_16_RGB555 = 5 ##< 16-bit RGB pixel format (5 bits per color)
|
||||
_16_ARGB1555 = 6 ##< 16-bit ARGB pixel format (5 bits per color + 1 bit for alpha)
|
||||
_16_ARGB4444 = 7 ##< 16-bit ARGB pixel format (4 bits per color)
|
||||
_8_RGB332 = 8 ##< 8-bit RGB pixel format
|
||||
_8_ARGB2222 = 9 ##< 8-bit ARGB pixel format
|
||||
_32_ABGR8888 = 10 ##< 32-bit ABGR pixel format
|
||||
_32_RGBA8888 = 11 ##< 32-bit RGBA pixel format
|
||||
_32_BGRA8888 = 12 ##< 32-bit BGRA pixel format
|
||||
_32_BGR888 = 13 ##< 32-bit BGR pixel format
|
||||
_24_BGR888 = 14 ##< 24-bit BGR pixel format
|
||||
_16_BGR565 = 15 ##< 16-bit BGR pixel format
|
||||
_16_BGR555 = 16 ##< 16-bit BGR pixel format (5 bits per color)
|
||||
_16_ABGR1555 = 17 ##< 16-bit ABGR pixel format (5 bits per color + 1 bit for alpha)
|
||||
_16_ABGR4444 = 18 ##< 16-bit ABGR pixel format (4 bits per color)
|
||||
_DXT1 = 19 ##< S3/DirectX Texture Compression 1
|
||||
_DXT2 = 20 ##< S3/DirectX Texture Compression 2
|
||||
_DXT3 = 21 ##< S3/DirectX Texture Compression 3
|
||||
_DXT4 = 22 ##< S3/DirectX Texture Compression 4
|
||||
_DXT5 = 23 ##< S3/DirectX Texture Compression 5
|
||||
_16_V8U8 = 24 ##< 16-bit Bump Map format format (8 bits per color)
|
||||
_32_V16U16 = 25 ##< 32-bit Bump Map format format (16 bits per color)
|
||||
_16_L6V5U5 = 26 ##< 16-bit Bump Map format format with luminance
|
||||
_32_X8L8V8U8 = 27 ##< 32-bit Bump Map format format with luminance
|
||||
_8_ABGR8888_CLUT = 28 ##< 8 bits indexed CLUT (ABGR)
|
||||
_8_ARGB8888_CLUT = 29 ##< 8 bits indexed CLUT (ARGB)
|
||||
_4_ABGR8888_CLUT = 30 ##< 4 bits indexed CLUT (ABGR)
|
||||
_4_ARGB8888_CLUT = 31 ##< 4 bits indexed CLUT (ARGB)
|
||||
# UNKNOWN_PF = 0
|
||||
# """Unknown pixel format"""
|
||||
_32_ARGB8888 = 1
|
||||
"""32-bit ARGB pixel format with alpha"""
|
||||
_32_RGB888 = 2
|
||||
"""32-bit RGB pixel format without alpha"""
|
||||
_24_RGB888 = 3
|
||||
"""24-bit RGB pixel format"""
|
||||
_16_RGB565 = 4
|
||||
"""16-bit RGB pixel format"""
|
||||
_16_RGB555 = 5
|
||||
"""16-bit RGB pixel format (5 bits per color)"""
|
||||
_16_ARGB1555 = 6
|
||||
"""16-bit ARGB pixel format (5 bits per color + 1 bit for alpha)"""
|
||||
_16_ARGB4444 = 7
|
||||
"""16-bit ARGB pixel format (4 bits per color)"""
|
||||
_8_RGB332 = 8
|
||||
"""8-bit RGB pixel format"""
|
||||
_8_ARGB2222 = 9
|
||||
"""8-bit ARGB pixel format"""
|
||||
_32_ABGR8888 = 10
|
||||
"""32-bit ABGR pixel format"""
|
||||
_32_RGBA8888 = 11
|
||||
"""32-bit RGBA pixel format"""
|
||||
_32_BGRA8888 = 12
|
||||
"""32-bit BGRA pixel format"""
|
||||
_32_BGR888 = 13
|
||||
"""32-bit BGR pixel format"""
|
||||
_24_BGR888 = 14
|
||||
"""24-bit BGR pixel format"""
|
||||
_16_BGR565 = 15
|
||||
"""16-bit BGR pixel format"""
|
||||
_16_BGR555 = 16
|
||||
"""16-bit BGR pixel format (5 bits per color)"""
|
||||
_16_ABGR1555 = 17
|
||||
"""16-bit ABGR pixel format (5 bits per color + 1 bit for alpha)"""
|
||||
_16_ABGR4444 = 18
|
||||
"""16-bit ABGR pixel format (4 bits per color)"""
|
||||
_DXT1 = 19
|
||||
"""S3/DirectX Texture Compression 1"""
|
||||
_DXT2 = 20
|
||||
"""S3/DirectX Texture Compression 2"""
|
||||
_DXT3 = 21
|
||||
"""S3/DirectX Texture Compression 3"""
|
||||
_DXT4 = 22
|
||||
"""S3/DirectX Texture Compression 4"""
|
||||
_DXT5 = 23
|
||||
"""S3/DirectX Texture Compression 5"""
|
||||
_16_V8U8 = 24
|
||||
"""16-bit Bump Map format format (8 bits per color)"""
|
||||
_32_V16U16 = 25
|
||||
"""32-bit Bump Map format format (16 bits per color)"""
|
||||
_16_L6V5U5 = 26
|
||||
"""16-bit Bump Map format format with luminance"""
|
||||
_32_X8L8V8U8 = 27
|
||||
"""32-bit Bump Map format format with luminance"""
|
||||
_8_ABGR8888_CLUT = 28
|
||||
"""8 bits indexed CLUT (ABGR)"""
|
||||
_8_ARGB8888_CLUT = 29
|
||||
"""8 bits indexed CLUT (ARGB)"""
|
||||
_4_ABGR8888_CLUT = 30
|
||||
"""4 bits indexed CLUT (ABGR)"""
|
||||
_4_ARGB8888_CLUT = 31
|
||||
"""4 bits indexed CLUT (ARGB)"""
|
||||
|
||||
class VXLIGHT_TYPE(enum.IntEnum):
|
||||
"""!
|
||||
Light type
|
||||
"""
|
||||
VX_LIGHTPOINT = 1 ##< The Light is a point of light
|
||||
VX_LIGHTSPOT = 2 ##< The light is a spotlight
|
||||
VX_LIGHTDIREC = 3 ##< The light is directional light : Lights comes from an infinite point so only direction of light can be given
|
||||
#VX_LIGHTPARA = 4 ##< Obsolete, do not use
|
||||
Light type.
|
||||
"""
|
||||
VX_LIGHTPOINT = 1
|
||||
"""The Light is a point of light"""
|
||||
VX_LIGHTSPOT = 2
|
||||
"""The light is a spotlight"""
|
||||
VX_LIGHTDIREC = 3
|
||||
"""The light is directional light : Lights comes from an infinite point so only direction of light can be given"""
|
||||
# VX_LIGHTPARA = 4
|
||||
# """Obsolete, do not use"""
|
||||
|
||||
class VXTEXTURE_BLENDMODE(enum.IntEnum):
|
||||
"""!
|
||||
Blend Mode Flags
|
||||
"""
|
||||
VXTEXTUREBLEND_DECAL = 1 ##< Texture replace any material information
|
||||
VXTEXTUREBLEND_MODULATE = 2 ##< Texture and material are combine. Alpha information of the texture replace material alpha component.
|
||||
VXTEXTUREBLEND_DECALALPHA = 3 ##< Alpha information in the texture specify how material and texture are combined. Alpha information of the texture replace material alpha component.
|
||||
VXTEXTUREBLEND_MODULATEALPHA = 4 ##< Alpha information in the texture specify how material and texture are combined
|
||||
Blend Mode Flags
|
||||
"""
|
||||
VXTEXTUREBLEND_DECAL = 1
|
||||
"""Texture replace any material information"""
|
||||
VXTEXTUREBLEND_MODULATE = 2
|
||||
"""Texture and material are combine. Alpha information of the texture replace material alpha component."""
|
||||
VXTEXTUREBLEND_DECALALPHA = 3
|
||||
"""Alpha information in the texture specify how material and texture are combined. Alpha information of the texture replace material alpha component."""
|
||||
VXTEXTUREBLEND_MODULATEALPHA = 4
|
||||
"""Alpha information in the texture specify how material and texture are combined"""
|
||||
VXTEXTUREBLEND_DECALMASK = 5
|
||||
VXTEXTUREBLEND_MODULATEMASK = 6
|
||||
VXTEXTUREBLEND_COPY = 7 ##< Equivalent to DECAL
|
||||
VXTEXTUREBLEND_COPY = 7
|
||||
"""Equivalent to DECAL"""
|
||||
VXTEXTUREBLEND_ADD = 8
|
||||
VXTEXTUREBLEND_DOTPRODUCT3 = 9 ##< Perform a Dot Product 3 between texture (normal map) and a referential vector given in VXRENDERSTATE_TEXTUREFACTOR.
|
||||
VXTEXTUREBLEND_DOTPRODUCT3 = 9
|
||||
"""Perform a Dot Product 3 between texture (normal map)and a referential vector given in VXRENDERSTATE_TEXTUREFACTOR."""
|
||||
VXTEXTUREBLEND_MAX = 10
|
||||
# VXTEXTUREBLEND_MASK = 0xF
|
||||
|
||||
class VXTEXTURE_FILTERMODE(enum.IntEnum):
|
||||
"""!
|
||||
"""
|
||||
Filter Mode Options
|
||||
"""
|
||||
VXTEXTUREFILTER_NEAREST = 1 ##< No Filter
|
||||
VXTEXTUREFILTER_LINEAR = 2 ##< Bilinear Interpolation
|
||||
VXTEXTUREFILTER_MIPNEAREST = 3 ##< Mip mapping
|
||||
VXTEXTUREFILTER_MIPLINEAR = 4 ##< Mip Mapping with Bilinear interpolation
|
||||
VXTEXTUREFILTER_LINEARMIPNEAREST = 5 ##< Mip Mapping with Bilinear interpolation between mipmap levels.
|
||||
VXTEXTUREFILTER_LINEARMIPLINEAR = 6 ##< Trilinear Filtering
|
||||
VXTEXTUREFILTER_ANISOTROPIC = 7 ##< Anisotropic filtering
|
||||
VXTEXTUREFILTER_NEAREST = 1
|
||||
"""No Filter"""
|
||||
VXTEXTUREFILTER_LINEAR = 2
|
||||
"""Bilinear Interpolation"""
|
||||
VXTEXTUREFILTER_MIPNEAREST = 3
|
||||
"""Mip mapping"""
|
||||
VXTEXTUREFILTER_MIPLINEAR = 4
|
||||
"""Mip Mapping with Bilinear interpolation"""
|
||||
VXTEXTUREFILTER_LINEARMIPNEAREST = 5
|
||||
"""Mip Mapping with Bilinear interpolation between mipmap levels."""
|
||||
VXTEXTUREFILTER_LINEARMIPLINEAR = 6
|
||||
"""Trilinear Filtering"""
|
||||
VXTEXTUREFILTER_ANISOTROPIC = 7
|
||||
"""Anisotropic filtering"""
|
||||
# VXTEXTUREFILTER_MASK = 0xF
|
||||
|
||||
class VXTEXTURE_ADDRESSMODE(enum.IntEnum):
|
||||
"""!
|
||||
"""
|
||||
Texture addressing modes.
|
||||
"""
|
||||
VXTEXTURE_ADDRESSWRAP = 1 ##< Default mesh wrap mode is used (see CKMesh::SetWrapMode)
|
||||
VXTEXTURE_ADDRESSMIRROR = 2 ##< Texture coordinates outside the range [0..1] are flipped evenly.
|
||||
VXTEXTURE_ADDRESSCLAMP = 3 ##< Texture coordinates greater than 1.0 are set to 1.0, and values less than 0.0 are set to 0.0.
|
||||
VXTEXTURE_ADDRESSBORDER = 4 ##< When texture coordinates are greater than 1.0 or less than 0.0 texture is set to a color defined in CKMaterial::SetTextureBorderColor.
|
||||
VXTEXTURE_ADDRESSMIRRORONCE = 5 ##<
|
||||
VXTEXTURE_ADDRESSWRAP = 1
|
||||
"""Default mesh wrap mode is used (see CKMesh::SetWrapMode)"""
|
||||
VXTEXTURE_ADDRESSMIRROR = 2
|
||||
"""Texture coordinates outside the range [0..1] are flipped evenly."""
|
||||
VXTEXTURE_ADDRESSCLAMP = 3
|
||||
"""Texture coordinates greater than 1.0 are set to 1.0, and values less than 0.0 are set to 0.0."""
|
||||
VXTEXTURE_ADDRESSBORDER = 4
|
||||
"""When texture coordinates are greater than 1.0 or less than 0.0 texture is set to a color defined in CKMaterial::SetTextureBorderColor."""
|
||||
VXTEXTURE_ADDRESSMIRRORONCE = 5
|
||||
""""""
|
||||
# VXTEXTURE_ADDRESSMASK = 0x7
|
||||
# """mask for all values"""
|
||||
|
||||
class VXBLEND_MODE(enum.IntEnum):
|
||||
"""!
|
||||
"""
|
||||
Blending Mode options
|
||||
"""
|
||||
VXBLEND_ZERO = 1 ##< Blend factor is (0, 0, 0, 0).
|
||||
VXBLEND_ONE = 2 ##< Blend factor is (1, 1, 1, 1).
|
||||
VXBLEND_SRCCOLOR = 3 ##< Blend factor is (Rs, Gs, Bs, As).
|
||||
VXBLEND_INVSRCCOLOR = 4 ##< Blend factor is (1-Rs, 1-Gs, 1-Bs, 1-As).
|
||||
VXBLEND_SRCALPHA = 5 ##< Blend factor is (As, As, As, As).
|
||||
VXBLEND_INVSRCALPHA = 6 ##< Blend factor is (1-As, 1-As, 1-As, 1-As).
|
||||
VXBLEND_DESTALPHA = 7 ##< Blend factor is (Ad, Ad, Ad, Ad).
|
||||
VXBLEND_INVDESTALPHA = 8 ##< Blend factor is (1-Ad, 1-Ad, 1-Ad, 1-Ad).
|
||||
VXBLEND_DESTCOLOR = 9 ##< Blend factor is (Rd, Gd, Bd, Ad).
|
||||
VXBLEND_INVDESTCOLOR = 10 ##< Blend factor is (1-Rd, 1-Gd, 1-Bd, 1-Ad).
|
||||
VXBLEND_SRCALPHASAT = 11 ##< Blend factor is (f, f, f, 1); f = min(As, 1-Ad).
|
||||
#VXBLEND_BOTHSRCALPHA = 12 ##< Source blend factor is (As, As, As, As) and destination blend factor is (1-As, 1-As, 1-As, 1-As)
|
||||
#VXBLEND_BOTHINVSRCALPHA = 13 ##< Source blend factor is (1-As, 1-As, 1-As, 1-As) and destination blend factor is (As, As, As, As)
|
||||
VXBLEND_ZERO = 1
|
||||
"""Blend factor is (0, 0, 0, 0)."""
|
||||
VXBLEND_ONE = 2
|
||||
"""Blend factor is (1, 1, 1, 1)."""
|
||||
VXBLEND_SRCCOLOR = 3
|
||||
"""Blend factor is (Rs, Gs, Bs, As)."""
|
||||
VXBLEND_INVSRCCOLOR = 4
|
||||
"""Blend factor is (1-Rs, 1-Gs, 1-Bs, 1-As)."""
|
||||
VXBLEND_SRCALPHA = 5
|
||||
"""Blend factor is (As, As, As, As)."""
|
||||
VXBLEND_INVSRCALPHA = 6
|
||||
"""Blend factor is (1-As, 1-As, 1-As, 1-As)."""
|
||||
VXBLEND_DESTALPHA = 7
|
||||
"""Blend factor is (Ad, Ad, Ad, Ad)."""
|
||||
VXBLEND_INVDESTALPHA = 8
|
||||
"""Blend factor is (1-Ad, 1-Ad, 1-Ad, 1-Ad)."""
|
||||
VXBLEND_DESTCOLOR = 9
|
||||
"""Blend factor is (Rd, Gd, Bd, Ad)."""
|
||||
VXBLEND_INVDESTCOLOR = 10
|
||||
"""Blend factor is (1-Rd, 1-Gd, 1-Bd, 1-Ad)."""
|
||||
VXBLEND_SRCALPHASAT = 11
|
||||
"""Blend factor is (f, f, f, 1); f = min(As, 1-Ad)."""
|
||||
# VXBLEND_BOTHSRCALPHA = 12
|
||||
# """Source blend factor is (As, As, As, As) and destination blend factor is (1-As, 1-As, 1-As, 1-As)"""
|
||||
# VXBLEND_BOTHINVSRCALPHA = 13
|
||||
# """Source blend factor is (1-As, 1-As, 1-As, 1-As) and destination blend factor is (As, As, As, As)"""
|
||||
# VXBLEND_MASK = 0xF
|
||||
# """Source blend factor is (1-As, 1-As, 1-As, 1-As) and destination blend factor is (As, As, As, As)"""
|
||||
|
||||
class VXFILL_MODE(enum.IntEnum):
|
||||
"""!
|
||||
Fill Mode Options
|
||||
"""
|
||||
VXFILL_POINT = 1 ##< Vertices rendering
|
||||
VXFILL_WIREFRAME = 2 ##< Edges rendering
|
||||
VXFILL_SOLID = 3 ##< Face rendering
|
||||
Fill Mode Options
|
||||
"""
|
||||
VXFILL_POINT = 1
|
||||
"""Vertices rendering"""
|
||||
VXFILL_WIREFRAME = 2
|
||||
"""Edges rendering"""
|
||||
VXFILL_SOLID = 3
|
||||
"""Face rendering"""
|
||||
# VXFILL_MASK = 3
|
||||
|
||||
class VXSHADE_MODE(enum.IntEnum):
|
||||
"""!
|
||||
"""
|
||||
Shade Mode Options
|
||||
"""
|
||||
VXSHADE_FLAT = 1 ##< Flat Shading
|
||||
VXSHADE_GOURAUD = 2 ##< Gouraud Shading
|
||||
VXSHADE_PHONG = 3 ##< Phong Shading (Not yet supported by most implementation)
|
||||
VXSHADE_FLAT = 1
|
||||
"""Flat Shading"""
|
||||
VXSHADE_GOURAUD = 2
|
||||
"""Gouraud Shading"""
|
||||
VXSHADE_PHONG = 3
|
||||
"""Phong Shading (Not yet supported by most implementation)"""
|
||||
# VXSHADE_MASK = 3
|
||||
|
||||
class VXCMPFUNC(enum.IntEnum):
|
||||
"""!
|
||||
"""
|
||||
Comparison Function
|
||||
"""
|
||||
VXCMP_NEVER = 1 ##< Always fail the test.
|
||||
VXCMP_LESS = 2 ##< Accept if value if less than current value.
|
||||
VXCMP_EQUAL = 3 ##< Accept if value if equal than current value.
|
||||
VXCMP_LESSEQUAL = 4 ##< Accept if value if less or equal than current value.
|
||||
VXCMP_GREATER = 5 ##< Accept if value if greater than current value.
|
||||
VXCMP_NOTEQUAL = 6 ##< Accept if value if different than current value.
|
||||
VXCMP_GREATEREQUAL = 7 ##< Accept if value if greater or equal current value.
|
||||
VXCMP_ALWAYS = 8 ##< Always accept the test.
|
||||
VXCMP_NEVER = 1
|
||||
"""Always fail the test."""
|
||||
VXCMP_LESS = 2
|
||||
"""Accept if value if less than current value."""
|
||||
VXCMP_EQUAL = 3
|
||||
"""Accept if value if equal than current value."""
|
||||
VXCMP_LESSEQUAL = 4
|
||||
"""Accept if value if less or equal than current value."""
|
||||
VXCMP_GREATER = 5
|
||||
"""Accept if value if greater than current value."""
|
||||
VXCMP_NOTEQUAL = 6
|
||||
"""Accept if value if different than current value."""
|
||||
VXCMP_GREATEREQUAL = 7
|
||||
"""Accept if value if greater or equal current value."""
|
||||
VXCMP_ALWAYS = 8
|
||||
"""Always accept the test."""
|
||||
# VXCMP_MASK = 0xF
|
||||
# """Mask for all possible values."""
|
||||
|
||||
class VXMESH_LITMODE(enum.IntEnum):
|
||||
"""!
|
||||
{filename:VXMESH_LITMODE}
|
||||
Summary: Mesh lighting options
|
||||
|
||||
Remarks:
|
||||
+ The VXMESH_LITMODE is used by CKMesh::SetLitMode to specify how lighting is done.
|
||||
See Also: CKMaterial,CKMesh
|
||||
"""
|
||||
VX_PRELITMESH = 0 ##< Lighting use color information store with vertices
|
||||
VX_LITMESH = 1 ##< Lighting is done by renderer using normals and face material information.
|
||||
Mesh lighting options
|
||||
"""
|
||||
VX_PRELITMESH = 0
|
||||
"""Lighting use color information store with vertices"""
|
||||
VX_LITMESH = 1
|
||||
"""Lighting is done by renderer using normals and face material information."""
|
||||
|
||||
class CK_CAMERA_PROJECTION(enum.IntEnum):
|
||||
CK_PERSPECTIVEPROJECTION = 1
|
||||
CK_ORTHOGRAPHICPROJECTION = 2
|
||||
|
||||
40
Assets/BMapBindings/pybmap/tests/cli.py
Normal file
40
Assets/BMapBindings/pybmap/tests/cli.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
class CliException(Exception):
|
||||
"""Error occurs when parsing test arguments."""
|
||||
pass
|
||||
|
||||
class Cli:
|
||||
|
||||
file_name: Path
|
||||
"""
|
||||
The path to the map for loading.
|
||||
"""
|
||||
ballance_dir: Path
|
||||
"""
|
||||
The path to the Ballance directory for finding textures
|
||||
"""
|
||||
encodings: tuple[str, ...]
|
||||
"""
|
||||
The name of encodings used by BMap for loading map.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
file_name = os.environ.get('BMAP_FILE_NAME', None)
|
||||
if file_name is None:
|
||||
raise CliException('You must specify BMAP_FILE_NAME environment variable before running this test.')
|
||||
else:
|
||||
self.file_name = Path(file_name).resolve()
|
||||
|
||||
ballance_dir = os.environ.get('BMAP_BALLANCE_DIR', None)
|
||||
if ballance_dir is None:
|
||||
raise CliException('You must specify BMAP_BALLANCE_DIR environment variable before running this test.')
|
||||
else:
|
||||
self.ballance_dir = Path(ballance_dir).resolve()
|
||||
|
||||
encodings = os.environ.get('BMAP_ENCODINGS', None)
|
||||
if encodings is None:
|
||||
raise CliException('You must specify BMAP_ENCODINGS environment variable before running this test.')
|
||||
else:
|
||||
self.encodings = tuple(encodings.split(','))
|
||||
35
Assets/BMapBindings/pybmap/tests/main.py
Normal file
35
Assets/BMapBindings/pybmap/tests/main.py
Normal file
@@ -0,0 +1,35 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
import pybmap.bmap_wrapper as bmap
|
||||
import cli
|
||||
import testsuits
|
||||
|
||||
def main() -> None:
|
||||
# Parse arguments
|
||||
try:
|
||||
cliopts = cli.Cli()
|
||||
except cli.CliException as e:
|
||||
print(f'Can not launch test. Reason: {e}')
|
||||
return
|
||||
|
||||
# Check BMap status.
|
||||
if not bmap.is_bmap_available():
|
||||
print('Fail to initialize native BMap.')
|
||||
return
|
||||
|
||||
# Waiting debugger
|
||||
input(f'Python PID is {os.getpid()}. Waiting for debugger, press any key to continue...')
|
||||
|
||||
# Start testbench
|
||||
with TemporaryDirectory() as tempdir:
|
||||
file_name = str(cliopts.file_name)
|
||||
temp_folder = str(tempdir)
|
||||
texture_folder = str(cliopts.ballance_dir / 'Textures')
|
||||
encodings = cliopts.encodings
|
||||
with bmap.BMFileReader(file_name, temp_folder, texture_folder, encodings) as reader:
|
||||
testsuits.TestCommon.test(reader)
|
||||
testsuits.TestEq.test(reader)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,178 +0,0 @@
|
||||
import os
|
||||
import argparse
|
||||
import PyBMap.bmap_wrapper as bmap
|
||||
|
||||
def main(file_name: str, temp_folder: str, texture_folder: str, encodings: tuple[str, ...]) -> None:
|
||||
input(f'Python PID is {os.getpid()}. Waiting for debugger, press any key to continue...')
|
||||
|
||||
# file_name: str = 'LightCameraTest.nmo'
|
||||
# temp_folder: str = 'Temp'
|
||||
# texture_folder: str = 'F:\\Ballance\\Ballance\\Textures'
|
||||
# encodings: tuple[str, ...] = ('cp1252', )
|
||||
with bmap.BMFileReader(file_name, temp_folder, texture_folder, encodings) as reader:
|
||||
test_common(reader)
|
||||
test_equatable(reader)
|
||||
|
||||
def test_common(reader: bmap.BMFileReader):
|
||||
# print('===== Groups =====')
|
||||
# for gp in reader.get_groups():
|
||||
# print(gp.get_name())
|
||||
# for gp_item in gp.get_objects():
|
||||
# print(f'\t{gp_item.get_name()}')
|
||||
|
||||
# print('===== 3dObjects =====')
|
||||
# for obj in reader.get_3dobjects():
|
||||
# print(obj.get_name())
|
||||
|
||||
# current_mesh = obj.get_current_mesh()
|
||||
# mesh_name = '<null>' if current_mesh is None else current_mesh.get_name()
|
||||
# print(f'\tMesh: {mesh_name}')
|
||||
# print(f'\tVisibility: {obj.get_visibility()}')
|
||||
# print(f'\tMatrix: {obj.get_world_matrix().to_const()}')
|
||||
|
||||
# print('===== Meshes =====')
|
||||
# for mesh in reader.get_meshs():
|
||||
# print(mesh.get_name())
|
||||
|
||||
# print(f'\tLit Mode: {mesh.get_lit_mode()}')
|
||||
# print(f'\tVertex Count: {mesh.get_vertex_count()}')
|
||||
# print(f'\tFace Count: {mesh.get_face_count()}')
|
||||
# print(f'\tMaterial Slot Count: {mesh.get_material_slot_count()}')
|
||||
|
||||
# print('===== Materials =====')
|
||||
# for mtl in reader.get_materials():
|
||||
# print(mtl.get_name())
|
||||
|
||||
# print(f'\tDiffuse: {mtl.get_diffuse().to_const_rgba()}')
|
||||
# print(f'\tAmbient: {mtl.get_ambient().to_const_rgba()}')
|
||||
# print(f'\tSpecular: {mtl.get_specular().to_const_rgba()}')
|
||||
# print(f'\tEmissive: {mtl.get_emissive().to_const_rgba()}')
|
||||
|
||||
# print(f'\tSpecular Power: {mtl.get_specular_power()}')
|
||||
|
||||
# print(f'\tTexture Border Color: {mtl.get_texture_border_color().to_const_rgba()}')
|
||||
|
||||
# print(f'\tTexture Blend Mode: {mtl.get_texture_blend_mode()}')
|
||||
# print(f'\tTexture Min Mode: {mtl.get_texture_min_mode()}')
|
||||
# print(f'\tTexture Mag Mode: {mtl.get_texture_mag_mode()}')
|
||||
# print(f'\tSource Blend: {mtl.get_source_blend()}')
|
||||
# print(f'\tDest Blend: {mtl.get_dest_blend()}')
|
||||
# print(f'\tFill Mode: {mtl.get_fill_mode()}')
|
||||
# print(f'\tShade Mode: {mtl.get_shade_mode()}')
|
||||
|
||||
# print(f'\tAlpha Test Enabled: {mtl.get_alpha_test_enabled()}')
|
||||
# print(f'\tAlpha Blend Enabled: {mtl.get_alpha_blend_enabled()}')
|
||||
# print(f'\tPerspective Correction Enabled: {mtl.get_perspective_correction_enabled()}')
|
||||
# print(f'\tZ Write Enabled: {mtl.get_z_write_enabled()}')
|
||||
# print(f'\tTwo Sided Enabled: {mtl.get_two_sided_enabled()}')
|
||||
|
||||
# print(f'\tAlpha Ref: {mtl.get_alpha_ref()}')
|
||||
|
||||
# print(f'\tAlpha Func: {mtl.get_alpha_func()}')
|
||||
# print(f'\tZ Func: {mtl.get_z_func()}')
|
||||
|
||||
# print('===== Textures =====')
|
||||
# for tex in reader.get_textures():
|
||||
# print(tex.get_name())
|
||||
|
||||
# print(f'\tFile Name: {tex.get_file_name()}')
|
||||
# print(f'\tSave Options: {tex.get_save_options()}')
|
||||
# print(f'\tVideo Format: {tex.get_video_format()}')
|
||||
|
||||
print('===== Target Lights =====')
|
||||
for lit in reader.get_target_lights():
|
||||
print(lit.get_name())
|
||||
|
||||
print(f'\tVisibility: {lit.get_visibility()}')
|
||||
print(f'\tMatrix: {lit.get_world_matrix().to_const()}')
|
||||
|
||||
print(f'\tType: {lit.get_type()}')
|
||||
print(f'\tColor: {lit.get_color().to_const_rgba()}')
|
||||
print(f'\tConstant Attenuation: {lit.get_constant_attenuation()}')
|
||||
print(f'\tLinear Attenuation: {lit.get_linear_attenuation()}')
|
||||
print(f'\tQuadratic Attenuation: {lit.get_quadratic_attenuation()}')
|
||||
print(f'\tRange: {lit.get_range()}')
|
||||
print(f'\tHot Spot: {lit.get_hot_spot()}')
|
||||
print(f'\tFalloff: {lit.get_falloff()}')
|
||||
print(f'\tFalloff Shape: {lit.get_falloff_shape()}')
|
||||
|
||||
print('===== END =====')
|
||||
|
||||
def test_equatable(reader: bmap.BMFileReader):
|
||||
# Check requirements
|
||||
assert (reader.get_3dobject_count() >= 2), '''
|
||||
Invalid file for test IEquatable.
|
||||
We can not perform IEquatable test because the length of 3dObject is too short (must greater than 2). Please choose another file to perform.
|
||||
'''
|
||||
|
||||
# Prepare variables
|
||||
all_3dobjects: tuple[bmap.BM3dObject, ...] = tuple(reader.get_3dobjects())
|
||||
first_3dobj: bmap.BM3dObject = all_3dobjects[0]
|
||||
second_3dobj: bmap.BM3dObject = all_3dobjects[1]
|
||||
all_3dobjects = tuple(reader.get_3dobjects())
|
||||
first_3dobj_again: bmap.BM3dObject = all_3dobjects[0]
|
||||
|
||||
# Test set
|
||||
test_set: set[bmap.BM3dObject] = set()
|
||||
|
||||
test_set.add(first_3dobj)
|
||||
assert len(test_set) == 1
|
||||
|
||||
assert first_3dobj in test_set
|
||||
assert first_3dobj_again in test_set
|
||||
assert second_3dobj not in test_set
|
||||
|
||||
test_set.add(first_3dobj_again)
|
||||
assert len(test_set) == 1
|
||||
test_set.add(second_3dobj)
|
||||
assert len(test_set) == 2
|
||||
|
||||
assert second_3dobj in test_set
|
||||
|
||||
# Test dict
|
||||
test_dict: dict[bmap.BM3dObject, str | None] = {}
|
||||
|
||||
test_dict[first_3dobj] = first_3dobj.get_name()
|
||||
assert len(test_dict) == 1
|
||||
|
||||
assert first_3dobj in test_dict
|
||||
assert first_3dobj_again in test_dict
|
||||
assert second_3dobj not in test_dict
|
||||
|
||||
test_dict[first_3dobj_again] = first_3dobj_again.get_name()
|
||||
assert len(test_dict) == 1
|
||||
test_dict[second_3dobj] = second_3dobj.get_name()
|
||||
assert len(test_dict) == 2
|
||||
|
||||
assert second_3dobj in test_dict
|
||||
|
||||
if __name__ == '__main__':
|
||||
# parse argument
|
||||
parser = argparse.ArgumentParser(
|
||||
prog='PyBMap Testbench',
|
||||
description='The testbench of PyBMap.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--file-path',
|
||||
action='store', dest='file_path', required=True,
|
||||
help='The path to input Virtools file.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--temp-dir',
|
||||
action='store', dest='temp_dir', required=True,
|
||||
help='The temp folder used by BMap.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--texture-dir',
|
||||
action='store', dest='texture_dir', required=True,
|
||||
help='The texture folder containing Ballance texture resources.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--encodings',
|
||||
action='extend', nargs='+', dest='encodings', required=True,
|
||||
help='The encodings used to parse the names stroed in input Virtools file.'
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# run main function
|
||||
main(args.file_path, args.temp_dir, args.texture_dir, tuple(args.encodings))
|
||||
177
Assets/BMapBindings/pybmap/tests/testsuits.py
Normal file
177
Assets/BMapBindings/pybmap/tests/testsuits.py
Normal file
@@ -0,0 +1,177 @@
|
||||
import pybmap.bmap_wrapper as bmap
|
||||
from pybmap.bmap_wrapper import BMFileReader
|
||||
|
||||
class TestCommon:
|
||||
@staticmethod
|
||||
def test(reader: BMFileReader) -> None:
|
||||
print('===== Groups =====')
|
||||
TestCommon.__test_group(reader)
|
||||
print('===== 3dObjects =====')
|
||||
TestCommon.__test_3dobject(reader)
|
||||
print('===== Meshes =====')
|
||||
TestCommon.__test_mesh(reader)
|
||||
print('===== Materials =====')
|
||||
TestCommon.__test_material(reader)
|
||||
print('===== Textures =====')
|
||||
TestCommon.__test_texture(reader)
|
||||
print('===== Target Lights =====')
|
||||
TestCommon.__test_target_light(reader)
|
||||
print('===== Target Cameras =====')
|
||||
TestCommon.__test_target_camera(reader)
|
||||
print('===== END =====')
|
||||
|
||||
@staticmethod
|
||||
def __test_group(reader: BMFileReader) -> None:
|
||||
for gp in reader.get_groups():
|
||||
print(gp.get_name())
|
||||
for gp_item in gp.get_objects():
|
||||
print(f'\t{gp_item.get_name()}')
|
||||
|
||||
@staticmethod
|
||||
def __test_3dobject(reader: BMFileReader) -> None:
|
||||
for obj in reader.get_3dobjects():
|
||||
print(obj.get_name())
|
||||
|
||||
current_mesh = obj.get_current_mesh()
|
||||
mesh_name = '<null>' if current_mesh is None else current_mesh.get_name()
|
||||
print(f'\tMesh: {mesh_name}')
|
||||
print(f'\tVisibility: {obj.get_visibility()}')
|
||||
print(f'\tMatrix: {obj.get_world_matrix().to_const()}')
|
||||
|
||||
@staticmethod
|
||||
def __test_mesh(reader: BMFileReader) -> None:
|
||||
for mesh in reader.get_meshs():
|
||||
print(mesh.get_name())
|
||||
|
||||
print(f'\tLit Mode: {mesh.get_lit_mode()}')
|
||||
print(f'\tVertex Count: {mesh.get_vertex_count()}')
|
||||
print(f'\tFace Count: {mesh.get_face_count()}')
|
||||
print(f'\tMaterial Slot Count: {mesh.get_material_slot_count()}')
|
||||
|
||||
@staticmethod
|
||||
def __test_material(reader: BMFileReader) -> None:
|
||||
for mtl in reader.get_materials():
|
||||
print(mtl.get_name())
|
||||
|
||||
print(f'\tDiffuse: {mtl.get_diffuse().to_const_rgba()}')
|
||||
print(f'\tAmbient: {mtl.get_ambient().to_const_rgba()}')
|
||||
print(f'\tSpecular: {mtl.get_specular().to_const_rgba()}')
|
||||
print(f'\tEmissive: {mtl.get_emissive().to_const_rgba()}')
|
||||
|
||||
print(f'\tSpecular Power: {mtl.get_specular_power()}')
|
||||
|
||||
print(f'\tTexture Border Color: {mtl.get_texture_border_color().to_const_rgba()}')
|
||||
|
||||
print(f'\tTexture Blend Mode: {mtl.get_texture_blend_mode()}')
|
||||
print(f'\tTexture Min Mode: {mtl.get_texture_min_mode()}')
|
||||
print(f'\tTexture Mag Mode: {mtl.get_texture_mag_mode()}')
|
||||
print(f'\tSource Blend: {mtl.get_source_blend()}')
|
||||
print(f'\tDest Blend: {mtl.get_dest_blend()}')
|
||||
print(f'\tFill Mode: {mtl.get_fill_mode()}')
|
||||
print(f'\tShade Mode: {mtl.get_shade_mode()}')
|
||||
|
||||
print(f'\tAlpha Test Enabled: {mtl.get_alpha_test_enabled()}')
|
||||
print(f'\tAlpha Blend Enabled: {mtl.get_alpha_blend_enabled()}')
|
||||
print(f'\tPerspective Correction Enabled: {mtl.get_perspective_correction_enabled()}')
|
||||
print(f'\tZ Write Enabled: {mtl.get_z_write_enabled()}')
|
||||
print(f'\tTwo Sided Enabled: {mtl.get_two_sided_enabled()}')
|
||||
|
||||
print(f'\tAlpha Ref: {mtl.get_alpha_ref()}')
|
||||
|
||||
print(f'\tAlpha Func: {mtl.get_alpha_func()}')
|
||||
print(f'\tZ Func: {mtl.get_z_func()}')
|
||||
|
||||
@staticmethod
|
||||
def __test_texture(reader: BMFileReader) -> None:
|
||||
for tex in reader.get_textures():
|
||||
print(tex.get_name())
|
||||
|
||||
print(f'\tFile Name: {tex.get_file_name()}')
|
||||
print(f'\tSave Options: {tex.get_save_options()}')
|
||||
print(f'\tVideo Format: {tex.get_video_format()}')
|
||||
|
||||
@staticmethod
|
||||
def __test_target_light(reader: BMFileReader) -> None:
|
||||
for lit in reader.get_target_lights():
|
||||
print(lit.get_name())
|
||||
|
||||
print(f'\tVisibility: {lit.get_visibility()}')
|
||||
print(f'\tMatrix: {lit.get_world_matrix().to_const()}')
|
||||
|
||||
print(f'\tType: {lit.get_type()}')
|
||||
print(f'\tColor: {lit.get_color().to_const_rgba()}')
|
||||
print(f'\tConstant Attenuation: {lit.get_constant_attenuation()}')
|
||||
print(f'\tLinear Attenuation: {lit.get_linear_attenuation()}')
|
||||
print(f'\tQuadratic Attenuation: {lit.get_quadratic_attenuation()}')
|
||||
print(f'\tRange: {lit.get_range()}')
|
||||
print(f'\tHot Spot: {lit.get_hot_spot()}')
|
||||
print(f'\tFalloff: {lit.get_falloff()}')
|
||||
print(f'\tFalloff Shape: {lit.get_falloff_shape()}')
|
||||
|
||||
@staticmethod
|
||||
def __test_target_camera(reader: BMFileReader) -> None:
|
||||
for cam in reader.get_target_cameras():
|
||||
print(cam.get_name())
|
||||
|
||||
print(f'\tVisibility: {cam.get_visibility()}')
|
||||
print(f'\tMatrix: {cam.get_world_matrix().to_const()}')
|
||||
|
||||
print(f'\tType: {cam.get_projection_type()}')
|
||||
print(f'\tOrthographic Zoom: {cam.get_orthographic_zoom()}')
|
||||
print(f'\tFront Plane: {cam.get_front_plane()}')
|
||||
print(f'\tBack Plane: {cam.get_back_plane()}')
|
||||
print(f'\tFov: {cam.get_fov()}')
|
||||
|
||||
(width, height) = cam.get_aspect_ratio()
|
||||
print(f'\tAspect Ratio: {width}:{height}')
|
||||
|
||||
class TestEq:
|
||||
@staticmethod
|
||||
def test(reader: BMFileReader) -> None:
|
||||
# Check requirements
|
||||
assert (reader.get_3dobject_count() >= 2), '''
|
||||
Invalid file for test __eq__.
|
||||
We can not perform __eq__ test because the length of 3dObject is too short (must greater than 2). Please choose another file to perform.
|
||||
'''
|
||||
|
||||
# Prepare variables
|
||||
all_3dobjects: tuple[bmap.BM3dObject, ...] = tuple(reader.get_3dobjects())
|
||||
first_3dobj: bmap.BM3dObject = all_3dobjects[0]
|
||||
second_3dobj: bmap.BM3dObject = all_3dobjects[1]
|
||||
all_3dobjects = tuple(reader.get_3dobjects())
|
||||
first_3dobj_again: bmap.BM3dObject = all_3dobjects[0]
|
||||
|
||||
# Test set
|
||||
test_set: set[bmap.BM3dObject] = set()
|
||||
|
||||
test_set.add(first_3dobj)
|
||||
assert len(test_set) == 1
|
||||
|
||||
assert first_3dobj in test_set
|
||||
assert first_3dobj_again in test_set
|
||||
assert second_3dobj not in test_set
|
||||
|
||||
test_set.add(first_3dobj_again)
|
||||
assert len(test_set) == 1
|
||||
test_set.add(second_3dobj)
|
||||
assert len(test_set) == 2
|
||||
|
||||
assert second_3dobj in test_set
|
||||
|
||||
# Test dict
|
||||
test_dict: dict[bmap.BM3dObject, str | None] = {}
|
||||
|
||||
test_dict[first_3dobj] = first_3dobj.get_name()
|
||||
assert len(test_dict) == 1
|
||||
|
||||
assert first_3dobj in test_dict
|
||||
assert first_3dobj_again in test_dict
|
||||
assert second_3dobj not in test_dict
|
||||
|
||||
test_dict[first_3dobj_again] = first_3dobj_again.get_name()
|
||||
assert len(test_dict) == 1
|
||||
test_dict[second_3dobj] = second_3dobj.get_name()
|
||||
assert len(test_dict) == 2
|
||||
|
||||
assert second_3dobj in test_dict
|
||||
|
||||
2
Assets/BMapBindings/pybmap/uv.lock
generated
2
Assets/BMapBindings/pybmap/uv.lock
generated
@@ -4,5 +4,5 @@ requires-python = ">=3.11"
|
||||
|
||||
[[package]]
|
||||
name = "pybmap"
|
||||
version = "0.1.0"
|
||||
version = "0.4.0"
|
||||
source = { editable = "." }
|
||||
|
||||
70
Assets/BMapBindings/rusty-bmap/Cargo.lock
generated
70
Assets/BMapBindings/rusty-bmap/Cargo.lock
generated
@@ -1,70 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "bmap"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"bmap-sys",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bmap-sys"
|
||||
version = "0.4.0"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
@@ -1,6 +0,0 @@
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
members = ["bmap","bmap-sys"]
|
||||
|
||||
[workspace.dependencies]
|
||||
thiserror = "2.0.12"
|
||||
@@ -1,9 +0,0 @@
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::os::raw::{c_float, c_void};
|
||||
//use std::ptr;
|
||||
|
||||
#[link(name = "BMap", kind = "dylib")]
|
||||
unsafe extern "C" {
|
||||
pub unsafe fn BMInit() -> bool;
|
||||
pub unsafe fn BMDispose() -> bool;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
use bmap_sys;
|
||||
|
||||
#[test]
|
||||
fn test_init_and_dispose() {
|
||||
assert!(unsafe { bmap_sys::BMInit() });
|
||||
assert!(unsafe { bmap_sys::BMDispose() });
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
[package]
|
||||
name = "bmap"
|
||||
version = "0.4.0"
|
||||
authors = ["yyc12345"]
|
||||
edition = "2024"
|
||||
description = "The wrapper for Rust binding to BMap."
|
||||
license = "SPDX:MIT"
|
||||
|
||||
[dependencies]
|
||||
thiserror = { workspace = true }
|
||||
bmap-sys = { path="../bmap-sys" }
|
||||
@@ -1 +0,0 @@
|
||||
use bmap_sys;
|
||||
@@ -50,6 +50,36 @@ CS_ENUM_LIKE: set[str] = set((
|
||||
"CK_CAMERA_PROJECTION",
|
||||
))
|
||||
|
||||
CPP_RS_TYPE_MAP: dict[str, str] = {
|
||||
"CKSTRING": "CKSTRING",
|
||||
"CKDWORD": "CKDWORD",
|
||||
"CKWORD": "CKWORD",
|
||||
"CKINT": "CKINT",
|
||||
"bool": "BMBOOL",
|
||||
"CKFLOAT": "CKFLOAT",
|
||||
"CKBYTE": "CKBYTE",
|
||||
"CK_ID": "CKID",
|
||||
"NakedOutputCallback": "BMCALLBACK",
|
||||
"BMFile": "BMVOID",
|
||||
"BMMeshTransition": "BMVOID",
|
||||
"VxVector3": "VxVector3",
|
||||
"VxVector2": "VxVector2",
|
||||
"VxColor": "VxColor",
|
||||
"VxMatrix": "VxMatrix",
|
||||
"CK_TEXTURE_SAVEOPTIONS": "CK_TEXTURE_SAVEOPTIONS",
|
||||
"VX_PIXELFORMAT": "VX_PIXELFORMAT",
|
||||
"VXLIGHT_TYPE": "VXLIGHT_TYPE",
|
||||
"VXTEXTURE_BLENDMODE": "VXTEXTURE_BLENDMODE",
|
||||
"VXTEXTURE_FILTERMODE": "VXTEXTURE_FILTERMODE",
|
||||
"VXTEXTURE_ADDRESSMODE": "VXTEXTURE_ADDRESSMODE",
|
||||
"VXBLEND_MODE": "VXBLEND_MODE",
|
||||
"VXFILL_MODE": "VXFILL_MODE",
|
||||
"VXSHADE_MODE": "VXSHADE_MODE",
|
||||
"VXCMPFUNC": "VXCMPFUNC",
|
||||
"VXMESH_LITMODE": "VXMESH_LITMODE",
|
||||
"CK_CAMERA_PROJECTION": "CK_CAMERA_PROJECTION",
|
||||
}
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CsInteropType:
|
||||
"""The class represent the C# type corresponding to extracted variable type."""
|
||||
@@ -229,6 +259,25 @@ class RenderUtils:
|
||||
# return value
|
||||
return CsInteropType(marshal_as, cs_type)
|
||||
|
||||
@staticmethod
|
||||
def get_rust_type(param: ExpFctParam) -> str:
|
||||
vt = param.var_type
|
||||
|
||||
# setup pointer level
|
||||
sb: str = 'P' * vt.get_pointer_level()
|
||||
# try getting cpp type from base type and add it
|
||||
cpp_type = CPP_RS_TYPE_MAP.get(vt.get_base_type(), None)
|
||||
if cpp_type is None:
|
||||
raise RuntimeError(f"unexpected type {vt.to_c_type()}")
|
||||
else:
|
||||
sb += cpp_type
|
||||
|
||||
# return built type string.
|
||||
if param.is_input:
|
||||
return f'param_in!({sb})'
|
||||
else:
|
||||
return f'param_out!({sb})'
|
||||
|
||||
|
||||
class TemplateRender:
|
||||
"""Render templates to code files"""
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
{%- for fct in payload.fcts %}
|
||||
{{ fct.fct_name }} = _create_bmap_func('{{ fct.fct_name }}', [
|
||||
{%- for param in fct.fct_params %}
|
||||
{{- utils.get_python_type(param) }}
|
||||
{%- if not loop.last %}, {% endif %}
|
||||
{%- endfor -%}
|
||||
])
|
||||
{{ fct.fct_name }} = _create_bmap_func('{{ fct.fct_name }}', ({% for param in fct.fct_params %}{{ utils.get_python_type(param) }}, {% endfor %}))
|
||||
"""
|
||||
{{ fct.fct_name }}
|
||||
{% for param in fct.fct_params %}
|
||||
:param {{ param.var_name }}: Direction: {% if param.is_input -%} input {%- else -%} output {%- endif %}. {{ param.var_desc }}
|
||||
:type {{ param.var_name }}: {{ utils.get_python_type(param) }} ({{ param.var_type.to_c_type() }} in C++). {% if param.is_input -%} Use ctypes.byref() pass it. {%- endif %}
|
||||
:type {{ param.var_name }}: {{ utils.get_python_type(param) }} ({{ param.var_type.to_c_type() }} in C++). {% if not param.is_input -%} Use ctypes.byref(data) pass it. {%- endif %}
|
||||
{%- endfor %}
|
||||
:return: True if no error, otherwise False.
|
||||
:rtype: bool
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
{%- for fct in payload.fcts %}
|
||||
/// {{ fct.fct_name }}
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
{%- for param in fct.fct_params %}
|
||||
/// - `{{ param.var_name }}`: Direction: {% if param.is_input -%} input {%- else -%} output {%- endif %}. C++ type: `{{ param.var_type.to_c_type() }}`. {{ param.var_desc }}
|
||||
{%- endfor %}
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// True if no error, otherwise False.
|
||||
pub unsafe fn {{ fct.fct_name }}(
|
||||
{%- for param in fct.fct_params -%}
|
||||
{{ param.var_name }}: {{ utils.get_rust_type(param) }}
|
||||
{%- if not loop.last %}, {% endif %}
|
||||
{%- endfor -%}
|
||||
) -> BMBOOL;
|
||||
{%- endfor %}
|
||||
|
||||
@@ -72,9 +72,13 @@ public class ClassidWalker extends CKDefinesParserBaseListener {
|
||||
mLevel = 0;
|
||||
mLevelStack = null;
|
||||
|
||||
// classid is signed int and do not have flags feature.
|
||||
mCurrentEnum.mCanUnsigned = false;
|
||||
mCurrentEnum.mUseFlags = false;
|
||||
// update self
|
||||
mCurrentEnum.updateByEntries();
|
||||
// we forcely set classid is signed and do not have flags feature.
|
||||
mCurrentEnum.mIsFlag = false;
|
||||
mCurrentEnum.mIsUnsigned = false;
|
||||
|
||||
// and return
|
||||
mResult = mCurrentEnum;
|
||||
mCurrentEnum = null;
|
||||
}
|
||||
@@ -90,6 +94,11 @@ public class ClassidWalker extends CKDefinesParserBaseListener {
|
||||
mCurrentEntry.mEntryName = ctx.CKGENERIC_ID(0).getText();
|
||||
mCurrentEntry.mEntryValue = ctx.CKGENERIC_NUM().getText();
|
||||
|
||||
// All classid number is positive.
|
||||
mCurrentEntry.mEntrySignKind = EnumsHelper.BEnumEntrySignKind.Positive;
|
||||
// And all in ordinary number style so it doesn't have flag feature.
|
||||
mCurrentEntry.mEntryFlagKind = EnumsHelper.BEnumEntryFlagKind.NotFlag;
|
||||
|
||||
// fill entry level info
|
||||
int this_level = getClassidLevel(ctx.getStart());
|
||||
if (this_level > mLevel) {
|
||||
|
||||
@@ -30,6 +30,9 @@ public class DefinesWalker extends CKDefinesParserBaseListener {
|
||||
|
||||
@Override
|
||||
public void exitProg(CKDefinesParser.ProgContext ctx) {
|
||||
// update enum
|
||||
mCurrentEnum.updateByEntries();
|
||||
// and return
|
||||
mResult = mCurrentEnum;
|
||||
mCurrentEnum = null;
|
||||
}
|
||||
@@ -48,19 +51,26 @@ public class DefinesWalker extends CKDefinesParserBaseListener {
|
||||
if (ctx.CKGENERIC_NUM() == null) {
|
||||
// define with id
|
||||
mCurrentEntry.mEntryValue = ctx.CKGENERIC_ID(1).getText();
|
||||
|
||||
// it refers other memeber, so its sign is unknown
|
||||
mCurrentEntry.mEntrySignKind = EnumsHelper.BEnumEntrySignKind.Unknown;
|
||||
// it refers other memeber, so it may flag.
|
||||
mCurrentEntry.mEntryFlagKind = EnumsHelper.BEnumEntryFlagKind.MayFlag;
|
||||
} else {
|
||||
// define with number
|
||||
String num = ctx.CKGENERIC_NUM().getText();
|
||||
mCurrentEntry.mEntryValue = num;
|
||||
|
||||
// check whether this enum can be unsigned
|
||||
// check the sign of this number
|
||||
if (CommonHelper.isNegativeNumber(num)) {
|
||||
mCurrentEnum.mCanUnsigned = false;
|
||||
mCurrentEntry.mEntrySignKind = EnumsHelper.BEnumEntrySignKind.Negative;
|
||||
} else {
|
||||
mCurrentEntry.mEntrySignKind = EnumsHelper.BEnumEntrySignKind.Positive;
|
||||
}
|
||||
// if the number is in hex form, this enum MIGHT have flags feature
|
||||
// if the number is in hex form, it may belong to flag enum
|
||||
if (CommonHelper.isHexNumber(num)) {
|
||||
mCurrentEnum.mUseFlags = true;
|
||||
mCurrentEntry.mEntryFlagKind = EnumsHelper.BEnumEntryFlagKind.MayFlag;
|
||||
} else {
|
||||
mCurrentEntry.mEntryFlagKind = EnumsHelper.BEnumEntryFlagKind.NotFlag;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,45 @@
|
||||
import java.util.Vector;
|
||||
|
||||
public class EnumsHelper {
|
||||
|
||||
/**
|
||||
* The kind of enum entry value.
|
||||
* This kind indicates whether this enum entry belong to a flag enum.
|
||||
*/
|
||||
public enum BEnumEntryFlagKind {
|
||||
/**
|
||||
* This enum entry can not belong to a flag enum.
|
||||
* Because its value is ordinary.
|
||||
*/
|
||||
NotFlag,
|
||||
/**
|
||||
* This enum entry may belong to a flag enum.
|
||||
* Because its value is in HEX format, and refering other members.
|
||||
*/
|
||||
MayFlag,
|
||||
/**
|
||||
* This enum entry must belong to a flag enum.
|
||||
* Because its value use bitwise operation.
|
||||
*/
|
||||
MustFlag,
|
||||
}
|
||||
|
||||
/**
|
||||
* The kind of enum entry value.
|
||||
* This kind indicates the sign of this enum entry value.
|
||||
*/
|
||||
public enum BEnumEntrySignKind {
|
||||
/** The value of this enum entry is positive number or zero. */
|
||||
Positive,
|
||||
/** The value of this enum entry is negative number. */
|
||||
Negative,
|
||||
/**
|
||||
* The value of this enum entry is unknown.
|
||||
* This is may be caused by that it refer other memeber.
|
||||
*/
|
||||
Unknown,
|
||||
}
|
||||
|
||||
/**
|
||||
* The struct to describe the entry of an enum.
|
||||
*/
|
||||
@@ -8,6 +47,8 @@ public class EnumsHelper {
|
||||
public BEnumEntry() {
|
||||
mEntryName = null;
|
||||
mEntryValue = null;
|
||||
mEntryFlagKind = null;
|
||||
mEntrySignKind = null;
|
||||
mEntryComment = null;
|
||||
}
|
||||
|
||||
@@ -15,6 +56,10 @@ public class EnumsHelper {
|
||||
public String mEntryName;
|
||||
/** The value of this entry. null if this entry do not have explicit value. */
|
||||
public String mEntryValue;
|
||||
/** The flag kind of this entry value. */
|
||||
public BEnumEntryFlagKind mEntryFlagKind;
|
||||
/** The sign kind of this entry value. */
|
||||
public BEnumEntrySignKind mEntrySignKind;
|
||||
/** The comment of this entry. null if no comment. */
|
||||
public String mEntryComment;
|
||||
}
|
||||
@@ -44,8 +89,8 @@ public class EnumsHelper {
|
||||
public BEnum() {
|
||||
mEnumName = null;
|
||||
mEnumComment = null;
|
||||
mCanUnsigned = true;
|
||||
mUseFlags = false;
|
||||
mIsUnsigned = true;
|
||||
mIsFlag = false;
|
||||
mEntries = new Vector<BEnumEntry>();
|
||||
}
|
||||
|
||||
@@ -53,12 +98,48 @@ public class EnumsHelper {
|
||||
public String mEnumName;
|
||||
/** The comment of this enum. null if no comment. */
|
||||
public String mEnumComment;
|
||||
/** True if this enum can use unsigned integer as its underlying type. */
|
||||
public boolean mCanUnsigned;
|
||||
/** True if this enum will use flags feature (supporting OR, AND, operators). */
|
||||
public boolean mUseFlags;
|
||||
/** True if this enum should use unsigned integer as its underlying type, otherwise false. */
|
||||
public boolean mIsUnsigned;
|
||||
/** True if this enum shoule have flags feature (supporting OR, AND, operators), otherwise false. */
|
||||
public boolean mIsFlag;
|
||||
/** The list to store entries of this enum. */
|
||||
public Vector<BEnumEntry> mEntries;
|
||||
|
||||
/**
|
||||
* Update some properties located in this class according to existing entries.
|
||||
*/
|
||||
public void updateByEntries() {
|
||||
// If there is at least one negative entry, the enum should be signed,
|
||||
// Otherwise, it is unsigned.
|
||||
// For unknown entries, ignore them.
|
||||
boolean has_negative = false;
|
||||
for (BEnumEntry entry : this.mEntries) {
|
||||
if (entry.mEntrySignKind == BEnumEntrySignKind.Negative) {
|
||||
has_negative = true;
|
||||
}
|
||||
}
|
||||
this.mIsUnsigned = !has_negative;
|
||||
|
||||
// For flag kind, if there is "Must Flag" entry, the enum should be a flag enum.
|
||||
// Then, if "May Flag" entry is more than "Not Flag", the enum would be a flag enum.
|
||||
// Otherwise, it is not flag.
|
||||
boolean has_must_flag = false;
|
||||
int cnt_may_flag = 0, cnt_not_flag = 0;
|
||||
for (BEnumEntry entry : this.mEntries) {
|
||||
switch (entry.mEntryFlagKind) {
|
||||
case NotFlag -> ++cnt_not_flag;
|
||||
case MayFlag -> ++cnt_may_flag;
|
||||
case MustFlag -> has_must_flag = true;
|
||||
}
|
||||
}
|
||||
if (has_must_flag) {
|
||||
this.mIsFlag = true;
|
||||
} else if (cnt_may_flag > cnt_not_flag) {
|
||||
this.mIsFlag = true;
|
||||
} else {
|
||||
this.mIsFlag = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -59,6 +59,8 @@ public class EnumsWalker extends CKEnumsParserBaseListener {
|
||||
List<TerminalNode> allNames = ctx.CKGENERIC_ID();
|
||||
mCurrentEnum.mEnumName = allNames.get(allNames.size() - 1).getText();
|
||||
|
||||
// update self and add into list
|
||||
mCurrentEnum.updateByEntries();
|
||||
mCurrentProg.mEnums.add(mCurrentEnum);
|
||||
mCurrentEnum = null;
|
||||
}
|
||||
@@ -75,6 +77,14 @@ public class EnumsWalker extends CKEnumsParserBaseListener {
|
||||
// get entry name
|
||||
mCurrentEntry.mEntryName = ctx.CKGENERIC_ID().getText();
|
||||
|
||||
// if its value is null, we manually fill 2 kinds
|
||||
if (mCurrentEntry.mEntryValue == null) {
|
||||
// the sign kind is unknown because it relys on other value (+1)
|
||||
mCurrentEntry.mEntrySignKind = EnumsHelper.BEnumEntrySignKind.Unknown;
|
||||
// because it just adds one from previous member, it should not belong to a flag enum
|
||||
mCurrentEntry.mEntryFlagKind = EnumsHelper.BEnumEntryFlagKind.NotFlag;
|
||||
}
|
||||
|
||||
mCurrentEnum.mEntries.add(mCurrentEntry);
|
||||
mCurrentEntry = null;
|
||||
}
|
||||
@@ -85,34 +95,42 @@ public class EnumsWalker extends CKEnumsParserBaseListener {
|
||||
List<TerminalNode> nums = ctx.CKGENERIC_NUM();
|
||||
|
||||
switch (nums.size()) {
|
||||
case 1: {
|
||||
// set value
|
||||
TerminalNode node = nums.get(0);
|
||||
mCurrentEntry.mEntryValue = node.getText();
|
||||
case 1: {
|
||||
// value is immediate number
|
||||
TerminalNode node = nums.get(0);
|
||||
String num = node.getText();
|
||||
mCurrentEntry.mEntryValue = num;
|
||||
|
||||
// check whether this enum can be unsigned
|
||||
if (CommonHelper.isNegativeNumber(node.getText())) {
|
||||
mCurrentEnum.mCanUnsigned = false;
|
||||
// check whether this enum can be unsigned
|
||||
if (CommonHelper.isNegativeNumber(num)) {
|
||||
mCurrentEntry.mEntrySignKind = EnumsHelper.BEnumEntrySignKind.Negative;
|
||||
} else {
|
||||
mCurrentEntry.mEntrySignKind = EnumsHelper.BEnumEntrySignKind.Positive;
|
||||
}
|
||||
// if the number is in hex form, this entry may belong to flag enum
|
||||
if (CommonHelper.isHexNumber(num)) {
|
||||
mCurrentEntry.mEntryFlagKind = EnumsHelper.BEnumEntryFlagKind.MayFlag;
|
||||
} else {
|
||||
mCurrentEntry.mEntryFlagKind = EnumsHelper.BEnumEntryFlagKind.NotFlag;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
// if the number is in hex form, this enum MIGHT have flags feature
|
||||
if (CommonHelper.isHexNumber(node.getText())) {
|
||||
mCurrentEnum.mUseFlags = true;
|
||||
case 2: {
|
||||
// value is bitwise operation
|
||||
TerminalNode num = nums.get(0), offset = nums.get(1);
|
||||
mCurrentEntry.mEntryValue = String.format("%s << %s", num.getText(), offset.getText());
|
||||
|
||||
// << operator appears.
|
||||
// it shoud be unsigned.
|
||||
mCurrentEntry.mEntrySignKind = EnumsHelper.BEnumEntrySignKind.Positive;
|
||||
// and it must belong to flag enum
|
||||
mCurrentEntry.mEntryFlagKind = EnumsHelper.BEnumEntryFlagKind.MustFlag;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
// set value
|
||||
TerminalNode num = nums.get(0), offset = nums.get(1);
|
||||
mCurrentEntry.mEntryValue = String.format("%s << %s", num.getText(), offset.getText());
|
||||
|
||||
// << operator appears. this enum must have flags feature
|
||||
mCurrentEnum.mUseFlags = true;
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected value: " + nums.size());
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected value: " + nums.size());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -123,9 +141,19 @@ public class EnumsWalker extends CKEnumsParserBaseListener {
|
||||
mCurrentEntry.mEntryValue = ctx.CKGENERIC_ID().stream().map(value -> value.getText())
|
||||
.collect(Collectors.joining(" | "));
|
||||
|
||||
// | operator appears. this enum must have flags feature
|
||||
mCurrentEnum.mUseFlags = true;
|
||||
|
||||
if (ctx.CKGENERIC_ID().size() > 1) {
|
||||
// If there is more than one ID, it means | operator appears.
|
||||
// It should be unsigned.
|
||||
mCurrentEntry.mEntrySignKind = EnumsHelper.BEnumEntrySignKind.Positive;
|
||||
// And it must belong to flag enum.
|
||||
mCurrentEntry.mEntryFlagKind = EnumsHelper.BEnumEntryFlagKind.MustFlag;
|
||||
} else {
|
||||
// Otherwise it just refer other member.
|
||||
// The sign of its value is unclear.
|
||||
mCurrentEntry.mEntrySignKind = EnumsHelper.BEnumEntrySignKind.Unknown;
|
||||
// And it may belong to flag enum because it refers other memeber.
|
||||
mCurrentEntry.mEntryFlagKind = EnumsHelper.BEnumEntryFlagKind.MayFlag;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,10 +6,28 @@ import com.google.gson.GsonBuilder;
|
||||
|
||||
public class JsonWriter {
|
||||
|
||||
private static String writeBEnumEntryFlagKind(EnumsHelper.BEnumEntryFlagKind kind) {
|
||||
return switch (kind) {
|
||||
case NotFlag -> "not-flag";
|
||||
case MayFlag -> "may-flag";
|
||||
case MustFlag -> "must-flag";
|
||||
};
|
||||
}
|
||||
|
||||
private static String writeBEnumEntrySignKind(EnumsHelper.BEnumEntrySignKind kind) {
|
||||
return switch (kind) {
|
||||
case Positive -> "positive";
|
||||
case Negative -> "negative";
|
||||
case Unknown -> "unknown";
|
||||
};
|
||||
}
|
||||
|
||||
private static JsonObject writeBEnumEntry(EnumsHelper.BEnumEntry enumEntry) {
|
||||
JsonObject data = new JsonObject();
|
||||
data.addProperty("name", enumEntry.mEntryName);
|
||||
data.addProperty("value", enumEntry.mEntryValue);
|
||||
data.addProperty("flag_kind", writeBEnumEntryFlagKind(enumEntry.mEntryFlagKind));
|
||||
data.addProperty("sign_kind", writeBEnumEntrySignKind(enumEntry.mEntrySignKind));
|
||||
data.addProperty("comment", enumEntry.mEntryComment);
|
||||
|
||||
// Export hierarchy if possible
|
||||
@@ -30,9 +48,8 @@ public class JsonWriter {
|
||||
JsonObject data = new JsonObject();
|
||||
data.addProperty("name", benum.mEnumName);
|
||||
data.addProperty("comment", benum.mEnumComment);
|
||||
data.addProperty("can_unsigned", benum.mCanUnsigned);
|
||||
data.addProperty("use_flags", benum.mUseFlags);
|
||||
data.addProperty("use_flags", benum.mUseFlags);
|
||||
data.addProperty("is_unsigned", benum.mIsUnsigned);
|
||||
data.addProperty("is_flag", benum.mIsFlag);
|
||||
|
||||
JsonArray entries = new JsonArray();
|
||||
for (EnumsHelper.BEnumEntry enumEntry : benum.mEntries) {
|
||||
|
||||
@@ -1,6 +1,47 @@
|
||||
import json
|
||||
import typing
|
||||
import utils
|
||||
import enum
|
||||
|
||||
|
||||
class BEnumEntryFlagKind(enum.StrEnum):
|
||||
"""
|
||||
The kind of enum entry value.
|
||||
This kind indicates whether this enum entry belong to a flag enum.
|
||||
"""
|
||||
|
||||
NotFlag = "not-flag"
|
||||
"""
|
||||
This enum entry can not belong to a flag enum.
|
||||
Because its value is ordinary.
|
||||
"""
|
||||
MayFlag = "may-flag"
|
||||
"""
|
||||
This enum entry may belong to a flag enum.
|
||||
Because its value is in HEX format, and refering other members.
|
||||
"""
|
||||
MustFlag = "must-flag"
|
||||
"""
|
||||
This enum entry must belong to a flag enum.
|
||||
Because its value use bitwise operation.
|
||||
"""
|
||||
|
||||
|
||||
class BEnumEntrySignKind(enum.StrEnum):
|
||||
"""
|
||||
The kind of enum entry value.
|
||||
This kind indicates the sign of this enum entry value.
|
||||
"""
|
||||
|
||||
Positive = "positive"
|
||||
"""The value of this enum entry is positive number or zero."""
|
||||
Negative = "negative"
|
||||
"""he value of this enum entry is negative number."""
|
||||
Unknown = "unknown"
|
||||
"""
|
||||
The value of this enum entry is unknown.
|
||||
This is may be caused by that it refer other memeber.
|
||||
"""
|
||||
|
||||
|
||||
class BEnumEntry:
|
||||
@@ -10,14 +51,25 @@ class BEnumEntry:
|
||||
"""The name of this entry."""
|
||||
__entry_value: str | None
|
||||
"""The value of this entry. None if this entry do not have explicit value."""
|
||||
__entry_flag_kind: BEnumEntryFlagKind
|
||||
"""The flag kind of this entry value."""
|
||||
__entry_sign_kind: BEnumEntrySignKind
|
||||
"""The sign kind of this entry value."""
|
||||
__entry_comment: str | None
|
||||
"""The comment of this entry. None if no comment."""
|
||||
|
||||
def __init__(
|
||||
self, entry_name: str, entry_value: str | None, entry_comment: str | None
|
||||
self,
|
||||
entry_name: str,
|
||||
entry_value: str | None,
|
||||
entry_flag_kind: BEnumEntryFlagKind,
|
||||
entry_sign_kind: BEnumEntrySignKind,
|
||||
entry_comment: str | None,
|
||||
):
|
||||
self.__entry_name = entry_name
|
||||
self.__entry_value = entry_value
|
||||
self.__entry_flag_kind = entry_flag_kind
|
||||
self.__entry_sign_kind = entry_sign_kind
|
||||
self.__entry_comment = entry_comment
|
||||
|
||||
def get_entry_name(self) -> str:
|
||||
@@ -37,6 +89,8 @@ class BEnumEntry:
|
||||
return BEnumEntry(
|
||||
data["name"],
|
||||
data.get("value", None),
|
||||
BEnumEntryFlagKind(data.get("flag_kind")),
|
||||
BEnumEntrySignKind(data.get("sign_kind")),
|
||||
data.get("comment", None),
|
||||
)
|
||||
|
||||
@@ -58,10 +112,12 @@ class BHierarchyEnumEntry(BEnumEntry):
|
||||
self,
|
||||
entry_name: str,
|
||||
entry_value: str | None,
|
||||
entry_flag_kind: BEnumEntryFlagKind,
|
||||
entry_sign_kind: BEnumEntrySignKind,
|
||||
entry_comment: str | None,
|
||||
hierarchy: list[str],
|
||||
):
|
||||
super().__init__(entry_name, entry_value, entry_comment)
|
||||
super().__init__(entry_name, entry_value, entry_flag_kind, entry_sign_kind, entry_comment)
|
||||
self.__hierarchy = hierarchy
|
||||
|
||||
def iter_hierarchy(self, benum: "BEnum") -> typing.Iterator["BHierarchyEnumEntry"]:
|
||||
@@ -75,6 +131,8 @@ class BHierarchyEnumEntry(BEnumEntry):
|
||||
return BHierarchyEnumEntry(
|
||||
data["name"],
|
||||
data.get("value", None),
|
||||
BEnumEntryFlagKind(data.get("flag_kind")),
|
||||
BEnumEntrySignKind(data.get("sign_kind")),
|
||||
data.get("comment", None),
|
||||
data["hierarchy"],
|
||||
)
|
||||
@@ -87,9 +145,9 @@ class BEnum:
|
||||
"""The name of this enum."""
|
||||
__enum_comment: str | None
|
||||
"""The comment of this enum. None if no comment."""
|
||||
__can_unsigned: bool
|
||||
__is_unsigned: bool
|
||||
"""True if this enum can use unsigned integer as its underlying type."""
|
||||
__use_flags: bool
|
||||
__is_flag: bool
|
||||
"""True if this enum will use flags feature (supporting OR, AND, operators)."""
|
||||
__entries: list[BEnumEntry]
|
||||
"""The list to store entries of this enum."""
|
||||
@@ -101,14 +159,14 @@ class BEnum:
|
||||
self,
|
||||
enum_name: str,
|
||||
enum_comment: str | None,
|
||||
can_unsigned: bool,
|
||||
use_flags: bool,
|
||||
is_unsigned: bool,
|
||||
is_flag: bool,
|
||||
entries: list[BEnumEntry],
|
||||
):
|
||||
self.__enum_name = enum_name
|
||||
self.__enum_comment = enum_comment
|
||||
self.__can_unsigned = can_unsigned
|
||||
self.__use_flags = use_flags
|
||||
self.__is_unsigned = is_unsigned
|
||||
self.__is_flag = is_flag
|
||||
self.__entries = entries
|
||||
self.__entries_map = {e.get_entry_name(): e for e in entries}
|
||||
|
||||
@@ -120,13 +178,13 @@ class BEnum:
|
||||
"""Get the comment of this enum. None if no comment."""
|
||||
return self.__enum_comment
|
||||
|
||||
def get_can_unsigned(self) -> bool:
|
||||
def is_unsigned(self) -> bool:
|
||||
"""True if this enum can use unsigned integer as its underlying type."""
|
||||
return self.__can_unsigned
|
||||
return self.__is_unsigned
|
||||
|
||||
def get_use_flags(self) -> bool:
|
||||
def is_flag(self) -> bool:
|
||||
"""True if this enum will use flags feature (supporting OR, AND, operators)."""
|
||||
return self.__use_flags
|
||||
return self.__is_flag
|
||||
|
||||
def iter_entries(self) -> typing.Iterator[BEnumEntry]:
|
||||
"""Get the iterator of entries of this enum."""
|
||||
@@ -140,8 +198,8 @@ class BEnum:
|
||||
return BEnum(
|
||||
data["name"],
|
||||
data.get("comment", None),
|
||||
data["can_unsigned"],
|
||||
data["use_flags"],
|
||||
data["is_unsigned"],
|
||||
data["is_flag"],
|
||||
list(map(lambda i: BEnum.__create_entry_by_content(i), data["entries"])),
|
||||
)
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ def main():
|
||||
render.render_cpp_enum("CKERROR.hpp", ckerror)
|
||||
render.render_py_enum("CKERROR.py", ckerror)
|
||||
render.render_cs_enum("CKERROR.cs", ckerror)
|
||||
render.render_rs_enum("CKERROR.rs", ckerror)
|
||||
render.render_cpp_ckerror_docstring("CKERROR.docstring.hpp", "CKERROR.docstring.cpp", ckerror)
|
||||
render.render_py_enum_docstring("CKERROR.docstring.py", ckerror)
|
||||
render.render_cs_enum_docstring("CKERROR.docstring.cs", ckerror)
|
||||
|
||||
@@ -83,6 +83,25 @@ class RenderUtils:
|
||||
"""
|
||||
return RenderUtils.REGEX_CS_TO_LITERAL_NUMBER.sub("", numstr, 1)
|
||||
|
||||
@staticmethod
|
||||
def to_rs_num_literal(numstr: str) -> str:
|
||||
"""
|
||||
Convert given string into Rust number literal style.
|
||||
|
||||
Number literal declaration in Rust is slightly different with C++.
|
||||
C++ support U and L but Rust use another complete suffix mode to decide the type of numeric literal (u32, i32 and etc).
|
||||
However, Rust can properly deduce the correct type of number literal,
|
||||
so we just need simply remove any suffix.
|
||||
|
||||
This function is only served for C# code generation.
|
||||
|
||||
:param numstr: The captured number.
|
||||
:return: The Rust style number string.
|
||||
"""
|
||||
# We reuse existing function
|
||||
return RenderUtils.to_py_num_literal(numstr)
|
||||
|
||||
|
||||
REGEX_PY_EXT_HUMANRDABLE_ENTRY_NAME: typing.ClassVar[re.Pattern] = re.compile("^[a-zA-Z0-9]+_")
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{%- for benum in payload.iter_enums() %}
|
||||
{%- if benum.get_enum_comment() is not none %}
|
||||
{{ benum.get_enum_comment() | block_comment('/// ') }}
|
||||
{%- endif %} {%- if benum.get_use_flags() %}
|
||||
{%- endif %} {%- if benum.is_flag() %}
|
||||
[Flags]{%- endif %}
|
||||
public enum {{ benum.get_enum_name() }} : {% if benum.get_can_unsigned() -%} uint {%- else -%} int {%- endif %} {
|
||||
public enum {{ benum.get_enum_name() }} : {% if benum.is_unsigned() -%} uint {%- else -%} int {%- endif %} {
|
||||
{%- for entry in benum.iter_entries() %}
|
||||
{%- if entry.get_entry_comment() is not none %}
|
||||
/// <summary>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
{{ benum.get_enum_comment() | block_comment(' * ') }}
|
||||
*/
|
||||
{%- endif %}
|
||||
enum class {{ benum.get_enum_name() }} : {% if benum.get_can_unsigned() -%} CKDWORD {%- else -%} CKINT {%- endif %} {
|
||||
enum class {{ benum.get_enum_name() }} : {% if benum.is_unsigned() -%} CKDWORD {%- else -%} CKINT {%- endif %} {
|
||||
{%- for entry in benum.iter_entries() %}
|
||||
{{ entry.get_entry_name() }} {%- if entry.get_entry_value() is not none %} = {{ entry.get_entry_value() }} {%- endif %}, {%- if entry.get_entry_comment() is not none %} /**< {{ entry.get_entry_comment() | line_comment }} */ {%- endif %}
|
||||
{%- endfor %}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
{%- for benum in payload.iter_enums() %}
|
||||
{%- if benum.get_enum_comment() is not none %}
|
||||
{{ benum.get_enum_comment() | block_comment('/// ') }}
|
||||
{%- endif %}
|
||||
{%- if not benum.is_flag() %}
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[repr({% if benum.is_unsigned() -%} u32 {%- else -%} i32 {%- endif %})]
|
||||
#[non_exhaustive]
|
||||
#[rustfmt::skip]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum {{ benum.get_enum_name() }} {
|
||||
{%- for entry in benum.iter_entries() %}
|
||||
{%- if entry.get_entry_comment() is not none %}
|
||||
/// {{ entry.get_entry_comment() | line_comment }}
|
||||
{%- endif %}
|
||||
{{ entry.get_entry_name() }} {%- if entry.get_entry_value() is not none %} = {{ utils.to_py_num_literal(entry.get_entry_value()) }} {%- endif %},
|
||||
{%- endfor %}
|
||||
}
|
||||
{%- else %}
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[repr(transparent)]
|
||||
#[rustfmt::skip]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct {{ benum.get_enum_name() }}({% if benum.is_unsigned() -%} u32 {%- else -%} i32 {%- endif %});
|
||||
|
||||
bitflags! {
|
||||
impl {{ benum.get_enum_name() }}: {% if benum.is_unsigned() -%} u32 {%- else -%} i32 {%- endif %} {
|
||||
{%- for entry in benum.iter_entries() %}
|
||||
{%- if entry.get_entry_comment() is not none %}
|
||||
/// {{ entry.get_entry_comment() | line_comment }}
|
||||
{%- endif %}
|
||||
const {{ entry.get_entry_name() }} {%- if entry.get_entry_value() is not none %} = {{ utils.to_py_num_literal(entry.get_entry_value()) }} {%- endif %};
|
||||
{%- endfor %}
|
||||
}
|
||||
}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
|
||||
@@ -31,6 +31,8 @@ target_compile_definitions(BMap
|
||||
PRIVATE
|
||||
BMAP_EXPORTING
|
||||
)
|
||||
# Remove any possible prefix (such as `lib` on Linux)
|
||||
set_target_properties(BMap PROPERTIES PREFIX "")
|
||||
|
||||
# Install binary and headers
|
||||
install(TARGETS BMap
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
#include "Rule.hpp"
|
||||
#include <VTAll.hpp>
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/carton/termcolor.hpp>
|
||||
#include <yycc/string/op.hpp>
|
||||
#include <yycc/patch/stream.hpp>
|
||||
#include <yycc/windows/console.hpp>
|
||||
#include <yycc/carton/termcolor.hpp>
|
||||
#include <yycc/carton/ironpad.hpp>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
|
||||
@@ -101,14 +102,16 @@ static void CheckRules(BMapInspector::Cli::Args& args, BMapInspector::Map::Level
|
||||
BMapInspector::Reporter::Reporter reporter;
|
||||
|
||||
// Get rule collection
|
||||
BMapInspector::Rule::Ruleset ruleset;
|
||||
BMapInspector::Rule::RuleCollection rule_collection;
|
||||
// Show rule infos
|
||||
std::cout << strop::printf(u8"Total %" PRIuSIZET " rule(s) are loaded.", ruleset.GetRuleCount()) << std::endl
|
||||
std::cout << strop::printf(u8"Total %" PRIuSIZET " rule(s) are loaded.", rule_collection.GetRuleCount()) << std::endl
|
||||
<< u8"Check may take few minutes. Please do not close this console..." << std::endl;
|
||||
|
||||
// Check rules one by one
|
||||
for (auto* rule : ruleset.GetRules()) {
|
||||
for (auto* rule : rule_collection.GetRules()) {
|
||||
reporter.EnterRule(rule->GetRuleName());
|
||||
rule->Check(reporter, level);
|
||||
reporter.LeaveRule();
|
||||
}
|
||||
|
||||
// Show report conclusion
|
||||
|
||||
@@ -11,15 +11,18 @@ PRIVATE
|
||||
Map.cpp
|
||||
Rule.cpp
|
||||
# Rules
|
||||
Rule/Shared.cpp
|
||||
Rule/GpRules.cpp
|
||||
Rule/ChirsRules.cpp
|
||||
Rule/YYCRules.cpp
|
||||
Rule/ZZQRules.cpp
|
||||
Rule/BBugRules.cpp
|
||||
Rule/SOneRules.cpp
|
||||
Rule/SSBRules.cpp
|
||||
Rule/LXRules.cpp
|
||||
Ruleset/Shared/Utility.cpp
|
||||
Ruleset/Shared/Name.cpp
|
||||
Ruleset/Shared/Sector.cpp
|
||||
Ruleset/Shared/DupCmp.cpp
|
||||
Ruleset/GpRules.cpp
|
||||
Ruleset/ChirsRules.cpp
|
||||
Ruleset/YYCRules.cpp
|
||||
Ruleset/ZZQRules.cpp
|
||||
Ruleset/BBugRules.cpp
|
||||
Ruleset/SOneRules.cpp
|
||||
Ruleset/SSBRules.cpp
|
||||
Ruleset/LXRules.cpp
|
||||
)
|
||||
# Setup headers
|
||||
target_sources(BMapInspector
|
||||
@@ -33,15 +36,18 @@ FILES
|
||||
Map.hpp
|
||||
Rule.hpp
|
||||
# Rules
|
||||
Rule/Shared.hpp
|
||||
Rule/GpRules.hpp
|
||||
Rule/ChirsRules.hpp
|
||||
Rule/YYCRules.hpp
|
||||
Rule/ZZQRules.hpp
|
||||
Rule/BBugRules.hpp
|
||||
Rule/SOneRules.hpp
|
||||
Rule/SSBRules.hpp
|
||||
Rule/LXRules.hpp
|
||||
Ruleset/Shared/Utility.hpp
|
||||
Ruleset/Shared/Name.hpp
|
||||
Ruleset/Shared/Sector.hpp
|
||||
Ruleset/Shared/DupCmp.hpp
|
||||
Ruleset/GpRules.hpp
|
||||
Ruleset/ChirsRules.hpp
|
||||
Ruleset/YYCRules.hpp
|
||||
Ruleset/ZZQRules.hpp
|
||||
Ruleset/BBugRules.hpp
|
||||
Ruleset/SOneRules.hpp
|
||||
Ruleset/SSBRules.hpp
|
||||
Ruleset/LXRules.hpp
|
||||
)
|
||||
# Setup header infomation
|
||||
target_include_directories(BMapInspector
|
||||
|
||||
@@ -1,54 +1,70 @@
|
||||
#include "Reporter.hpp"
|
||||
#include <yycc/string/op.hpp>
|
||||
#include <cstdarg>
|
||||
#include <stdexcept>
|
||||
|
||||
using BMapInspector::Utils::ReportLevel;
|
||||
namespace strop = yycc::string::op;
|
||||
|
||||
namespace BMapInspector::Reporter {
|
||||
|
||||
#pragma region Reporter
|
||||
|
||||
Reporter::Reporter() {}
|
||||
Reporter::Reporter() : current_rule(std::nullopt), reports() {}
|
||||
|
||||
Reporter::~Reporter() {}
|
||||
|
||||
void Reporter::AddReport(Utils::ReportLevel level, const std::u8string_view &rule, const std::u8string_view &content) {
|
||||
this->reports.emplace_back(Report{
|
||||
.level = level,
|
||||
.rule = std::u8string(rule),
|
||||
.content = std::u8string(content),
|
||||
});
|
||||
void Reporter::EnterRule(const std::u8string_view &rule) {
|
||||
if (this->current_rule.has_value()) throw std::logic_error("can not enter rule multiple times");
|
||||
else this->current_rule = std::u8string(rule);
|
||||
}
|
||||
|
||||
void Reporter::WriteInfo(const std::u8string_view &rule, const std::u8string_view &content) {
|
||||
this->AddReport(Utils::ReportLevel::Info, rule, content);
|
||||
void Reporter::LeaveRule() {
|
||||
if (this->current_rule.has_value()) this->current_rule = std::nullopt;
|
||||
else throw std::logic_error("can not leave rule without any existing rule");
|
||||
}
|
||||
|
||||
void Reporter::FormatInfo(const std::u8string_view &rule, const char8_t *fmt, ...) {
|
||||
void Reporter::AddReport(ReportLevel level, const std::u8string_view &content) {
|
||||
if (this->current_rule.has_value()) {
|
||||
this->reports.emplace_back(Report{
|
||||
.level = level,
|
||||
.rule = std::u8string(this->current_rule.value()),
|
||||
.content = std::u8string(content),
|
||||
});
|
||||
} else {
|
||||
throw std::logic_error("can not add report without any rule scope");
|
||||
}
|
||||
}
|
||||
|
||||
void Reporter::WriteInfo(const std::u8string_view &content) {
|
||||
this->AddReport(ReportLevel::Info, content);
|
||||
}
|
||||
|
||||
void Reporter::FormatInfo(const char8_t *fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, fmt);
|
||||
this->WriteInfo(rule, strop::vprintf(fmt, argptr));
|
||||
this->WriteInfo(strop::vprintf(fmt, argptr));
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
void Reporter::WriteWarning(const std::u8string_view &rule, const std::u8string_view &content) {
|
||||
this->AddReport(Utils::ReportLevel::Warning, rule, content);
|
||||
void Reporter::WriteWarning(const std::u8string_view &content) {
|
||||
this->AddReport(ReportLevel::Warning, content);
|
||||
}
|
||||
void Reporter::FormatWarning(const std::u8string_view &rule, const char8_t *fmt, ...) {
|
||||
void Reporter::FormatWarning(const char8_t *fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, fmt);
|
||||
this->WriteWarning(rule, strop::vprintf(fmt, argptr));
|
||||
this->WriteWarning(strop::vprintf(fmt, argptr));
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
void Reporter::WriteError(const std::u8string_view &rule, const std::u8string_view &content) {
|
||||
this->AddReport(Utils::ReportLevel::Error, rule, content);
|
||||
void Reporter::WriteError(const std::u8string_view &content) {
|
||||
this->AddReport(ReportLevel::Error, content);
|
||||
}
|
||||
|
||||
void Reporter::FormatError(const std::u8string_view &rule, const char8_t *fmt, ...) {
|
||||
void Reporter::FormatError(const char8_t *fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, fmt);
|
||||
this->WriteError(rule, strop::vprintf(fmt, argptr));
|
||||
this->WriteError(strop::vprintf(fmt, argptr));
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
@@ -56,13 +72,13 @@ namespace BMapInspector::Reporter {
|
||||
ReporterDigest digest{.cnt_err = 0, .cnt_warn = 0, .cnt_info = 0};
|
||||
for (const auto &report : this->reports) {
|
||||
switch (report.level) {
|
||||
case Utils::ReportLevel::Error:
|
||||
case ReportLevel::Error:
|
||||
++digest.cnt_err;
|
||||
break;
|
||||
case Utils::ReportLevel::Warning:
|
||||
case ReportLevel::Warning:
|
||||
++digest.cnt_warn;
|
||||
break;
|
||||
case Utils::ReportLevel::Info:
|
||||
case ReportLevel::Info:
|
||||
++digest.cnt_info;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
namespace BMapInspector::Reporter {
|
||||
|
||||
@@ -26,22 +27,27 @@ namespace BMapInspector::Reporter {
|
||||
~Reporter();
|
||||
YYCC_DEFAULT_COPY_MOVE(Reporter)
|
||||
|
||||
public:
|
||||
void EnterRule(const std::u8string_view& rule);
|
||||
void LeaveRule();
|
||||
|
||||
private:
|
||||
void AddReport(Utils::ReportLevel level, const std::u8string_view& rule, const std::u8string_view& content);
|
||||
void AddReport(Utils::ReportLevel level, const std::u8string_view& content);
|
||||
|
||||
public:
|
||||
void WriteInfo(const std::u8string_view& rule, const std::u8string_view& content);
|
||||
void FormatInfo(const std::u8string_view& rule, const char8_t* fmt, ...);
|
||||
void WriteWarning(const std::u8string_view& rule, const std::u8string_view& content);
|
||||
void FormatWarning(const std::u8string_view& rule, const char8_t* fmt, ...);
|
||||
void WriteError(const std::u8string_view& rule, const std::u8string_view& content);
|
||||
void FormatError(const std::u8string_view& rule, const char8_t* fmt, ...);
|
||||
void WriteInfo(const std::u8string_view& content);
|
||||
void FormatInfo(const char8_t* fmt, ...);
|
||||
void WriteWarning(const std::u8string_view& content);
|
||||
void FormatWarning(const char8_t* fmt, ...);
|
||||
void WriteError(const std::u8string_view& content);
|
||||
void FormatError(const char8_t* fmt, ...);
|
||||
|
||||
public:
|
||||
ReporterDigest GetDigest() const;
|
||||
const std::vector<Report>& GetReports() const;
|
||||
|
||||
private:
|
||||
std::optional<std::u8string> current_rule;
|
||||
std::vector<Report> reports;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#include "Rule.hpp"
|
||||
|
||||
#include "Rule/GpRules.hpp"
|
||||
#include "Rule/ChirsRules.hpp"
|
||||
#include "Rule/YYCRules.hpp"
|
||||
#include "Rule/BBugRules.hpp"
|
||||
#include "Rule/ZZQRules.hpp"
|
||||
#include "Rule/SOneRules.hpp"
|
||||
#include "Rule/SSBRules.hpp"
|
||||
#include "Rule/LXRules.hpp"
|
||||
#include "Ruleset/GpRules.hpp"
|
||||
#include "Ruleset/ChirsRules.hpp"
|
||||
#include "Ruleset/YYCRules.hpp"
|
||||
#include "Ruleset/BBugRules.hpp"
|
||||
#include "Ruleset/ZZQRules.hpp"
|
||||
#include "Ruleset/SOneRules.hpp"
|
||||
#include "Ruleset/SSBRules.hpp"
|
||||
#include "Ruleset/LXRules.hpp"
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
|
||||
@@ -19,38 +19,46 @@ namespace BMapInspector::Rule {
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Ruleset
|
||||
#pragma region RuleCollection
|
||||
|
||||
Ruleset::Ruleset() : rules() {
|
||||
RuleCollection::RuleCollection() : rules() {
|
||||
// Add rule into list.
|
||||
//rules.emplace_back(new GpRule1());
|
||||
//rules.emplace_back(new GpRule2());
|
||||
//rules.emplace_back(new Gp3Rule());
|
||||
//rules.emplace_back(new Chirs1Rule());
|
||||
rules.emplace_back(new YYCRule1());
|
||||
rules.emplace_back(new YYCRule2());
|
||||
rules.emplace_back(new BBugRule1());
|
||||
rules.emplace_back(new SOneRule1());
|
||||
rules.emplace_back(new SSBRule1());
|
||||
rules.emplace_back(new LXRule1());
|
||||
rules.emplace_back(new Ruleset::GpRule1());
|
||||
rules.emplace_back(new Ruleset::GpRule2());
|
||||
rules.emplace_back(new Ruleset::ChirsRule1());
|
||||
rules.emplace_back(new Ruleset::YYCRule1());
|
||||
rules.emplace_back(new Ruleset::YYCRule2());
|
||||
rules.emplace_back(new Ruleset::YYCRule3());
|
||||
rules.emplace_back(new Ruleset::YYCRule4());
|
||||
rules.emplace_back(new Ruleset::YYCRule5());
|
||||
rules.emplace_back(new Ruleset::YYCRule6());
|
||||
rules.emplace_back(new Ruleset::BBugRule1());
|
||||
rules.emplace_back(new Ruleset::BBugRule2());
|
||||
rules.emplace_back(new Ruleset::BBugRule3());
|
||||
rules.emplace_back(new Ruleset::ZZQRule1());
|
||||
rules.emplace_back(new Ruleset::ZZQRule2());
|
||||
rules.emplace_back(new Ruleset::ZZQRule3());
|
||||
rules.emplace_back(new Ruleset::SOneRule1());
|
||||
rules.emplace_back(new Ruleset::SSBRule1());
|
||||
rules.emplace_back(new Ruleset::LXRule1());
|
||||
// Add more rules...
|
||||
}
|
||||
|
||||
Ruleset::~Ruleset() {
|
||||
RuleCollection::~RuleCollection() {
|
||||
// Free rule from list.
|
||||
for (const auto* rule : this->rules) {
|
||||
delete rule;
|
||||
}
|
||||
}
|
||||
|
||||
size_t Ruleset::GetRuleCount() const {
|
||||
size_t RuleCollection::GetRuleCount() const {
|
||||
return this->rules.size();
|
||||
}
|
||||
|
||||
const std::vector<IRule*>& Ruleset::GetRules() const {
|
||||
const std::vector<IRule*>& RuleCollection::GetRules() const {
|
||||
return this->rules;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Ruleset
|
||||
} // namespace BMapInspector::Rule
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
|
||||
/**
|
||||
* @brief The interface of a rule.
|
||||
*/
|
||||
class IRule {
|
||||
public:
|
||||
IRule();
|
||||
@@ -21,18 +24,21 @@ namespace BMapInspector::Rule {
|
||||
virtual void Check(Reporter::Reporter& reporter, Map::Level& level) const = 0;
|
||||
};
|
||||
|
||||
class Ruleset {
|
||||
/**
|
||||
* @brief A collection of rules.
|
||||
*/
|
||||
class RuleCollection {
|
||||
public:
|
||||
Ruleset();
|
||||
~Ruleset();
|
||||
YYCC_DELETE_COPY_MOVE(Ruleset)
|
||||
RuleCollection();
|
||||
~RuleCollection();
|
||||
YYCC_DELETE_COPY_MOVE(RuleCollection)
|
||||
|
||||
public:
|
||||
size_t GetRuleCount() const;
|
||||
const std::vector<IRule *> &GetRules() const;
|
||||
const std::vector<IRule*>& GetRules() const;
|
||||
|
||||
private:
|
||||
std::vector<IRule *> rules;
|
||||
std::vector<IRule*> rules;
|
||||
};
|
||||
|
||||
} // namespace BMapInspector::Ruleset
|
||||
} // namespace BMapInspector::Rule
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
#include "BBugRules.hpp"
|
||||
#include "Shared.hpp"
|
||||
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
|
||||
#pragma region BBug Rule 1
|
||||
|
||||
constexpr char8_t BBUG1[] = u8"BBUG1";
|
||||
|
||||
BBugRule1::BBugRule1() : IRule() {}
|
||||
|
||||
BBugRule1::~BBugRule1() {}
|
||||
|
||||
std::u8string_view BBugRule1::GetRuleName() const {
|
||||
return BBUG1;
|
||||
}
|
||||
|
||||
void BBugRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
if (!level.GetTargetLights().empty()) {
|
||||
reporter.WriteInfo(BBUG1, u8"Using light in map is not suggested.");
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Rule
|
||||
@@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
#include "../Rule.hpp"
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
|
||||
/**
|
||||
* @brief BBug Rule 1
|
||||
* @details
|
||||
* Using light in map is not suggested.
|
||||
*/
|
||||
class BBugRule1 : public IRule {
|
||||
public:
|
||||
BBugRule1();
|
||||
virtual ~BBugRule1();
|
||||
YYCC_DELETE_COPY_MOVE(BBugRule1)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
#include "ChirsRules.hpp"
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
Chirs1Rule::Chirs1Rule() : IRule() {}
|
||||
Chirs1Rule::~Chirs1Rule() {}
|
||||
std::u8string_view Chirs1Rule::GetRuleName() const {
|
||||
return u8"CHIRS1";
|
||||
}
|
||||
void Chirs1Rule::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
// Report error if there is some material named Laterne_Verlauf
|
||||
// but its texture is not pointed to Laterne_Verlauf texture.
|
||||
|
||||
// Report error if some materials' texture is Laterne_Verlauf,
|
||||
// but its name is not Laterne_Verlauf.
|
||||
|
||||
// Report error if there is multiple Laterne_Verlauf material.
|
||||
}
|
||||
} // namespace BMapInspector::Rule
|
||||
@@ -1,53 +0,0 @@
|
||||
#include "GpRules.hpp"
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
|
||||
#pragma region GP1 Rule
|
||||
|
||||
GpRule1::GpRule1() : IRule() {}
|
||||
|
||||
GpRule1::~GpRule1() {}
|
||||
|
||||
std::u8string_view GpRule1::GetRuleName() const {
|
||||
return u8"GP1";
|
||||
}
|
||||
|
||||
void GpRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const {}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region GP2 Rule
|
||||
|
||||
constexpr char8_t GP2[] = u8"GP2";
|
||||
|
||||
GpRule2::GpRule2() : IRule() {}
|
||||
|
||||
GpRule2::~GpRule2() {}
|
||||
|
||||
std::u8string_view GpRule2::GetRuleName() const {
|
||||
return GP2;
|
||||
}
|
||||
|
||||
void GpRule2::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region GP3 Rule
|
||||
//
|
||||
// Gp3Rule::Gp3Rule() : IRule() {}
|
||||
//
|
||||
// Gp3Rule::~Gp3Rule() {}
|
||||
//
|
||||
// std::u8string_view Gp3Rule::GetRuleName() const {
|
||||
// return u8"GP3";
|
||||
// }
|
||||
//
|
||||
// void Gp3Rule::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
// // TODO: Mesh hash is not implemented.
|
||||
// }
|
||||
//
|
||||
//#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Rule
|
||||
@@ -1,63 +0,0 @@
|
||||
#pragma once
|
||||
#include "../Rule.hpp"
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
|
||||
// Reference: https://tieba.baidu.com/p/3182981807
|
||||
|
||||
/**
|
||||
* @brief Gamepiaynmo Rule 1
|
||||
* @details
|
||||
* The most comprehensive group checker inspired from Ballance Blender Plugin.
|
||||
*/
|
||||
class GpRule1 : public IRule {
|
||||
public:
|
||||
GpRule1();
|
||||
virtual ~GpRule1();
|
||||
YYCC_DELETE_COPY_MOVE(GpRule1)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Gamepiaynmo Rule 2
|
||||
* @details
|
||||
* Every Ballance group should not have any groups with same name.
|
||||
*/
|
||||
class GpRule2 : public IRule {
|
||||
public:
|
||||
GpRule2();
|
||||
virtual ~GpRule2();
|
||||
YYCC_DELETE_COPY_MOVE(GpRule2)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
///**
|
||||
// * @brief Gamepiaynmo Rule 2
|
||||
// * @details
|
||||
// * This rule make sure that one Ballance element must be grouped into only one sector group.
|
||||
// * Multiple grouping and none grouping will throw error.
|
||||
// */
|
||||
///**
|
||||
// * @brief Gamepiaynmo Rule 3
|
||||
// * @details
|
||||
// * This rule make sure that all Ballance element is grouped into correct element group.
|
||||
// * This rule will check the mesh of PH and guess which element it is.
|
||||
// */
|
||||
//class Gp3Rule : public IRule {
|
||||
//public:
|
||||
// Gp3Rule();
|
||||
// virtual ~Gp3Rule();
|
||||
// YYCC_DELETE_COPY_MOVE(Gp3Rule)
|
||||
|
||||
//public:
|
||||
// std::u8string_view GetRuleName() const override;
|
||||
// void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
//};
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
#include "SOneRules.hpp"
|
||||
#include "Shared.hpp"
|
||||
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
|
||||
#pragma region SOne Rule 1
|
||||
|
||||
constexpr char8_t SONE1[] = u8"SONE1";
|
||||
|
||||
SOneRule1::SOneRule1() : IRule() {}
|
||||
|
||||
SOneRule1::~SOneRule1() {}
|
||||
|
||||
std::u8string_view SOneRule1::GetRuleName() const {
|
||||
return SONE1;
|
||||
}
|
||||
|
||||
void SOneRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
auto* ctx = level.GetCKContext();
|
||||
auto physicalized_3dobjects = Shared::FetchPhysicalized3dObjects(ctx);
|
||||
|
||||
for (auto* physicalized_3dobject : physicalized_3dobjects) {
|
||||
auto* mesh = physicalized_3dobject->GetCurrentMesh();
|
||||
if (mesh == nullptr) {
|
||||
reporter.FormatError(
|
||||
SONE1,
|
||||
u8R"(Object "%s" is grouped into physicalization group, but it doesn't have any associated mesh. This will cause itself and following objects can not be physicalized.)",
|
||||
Shared::RenderObjectName(physicalized_3dobject));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Rule
|
||||
@@ -1,110 +0,0 @@
|
||||
#pragma once
|
||||
#include <VTAll.hpp>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
namespace BMapInspector::Rule::Shared {
|
||||
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
|
||||
#pragma region Constant Values
|
||||
|
||||
namespace GroupNames {
|
||||
// clang-format off
|
||||
constexpr char8_t PHYS_FLOORS[] = u8"Phys_Floors";
|
||||
constexpr char8_t PHYS_FLOORRAILS[] = u8"Phys_FloorRails";
|
||||
constexpr char8_t PHYS_FLOORSTOPPER[] = u8"Phys_FloorStopper";
|
||||
|
||||
constexpr std::array ALL_PH{
|
||||
u8"P_Extra_Life",
|
||||
u8"P_Extra_Point",
|
||||
u8"P_Trafo_Paper",
|
||||
u8"P_Trafo_Stone",
|
||||
u8"P_Trafo_Wood",
|
||||
u8"P_Ball_Paper",
|
||||
u8"P_Ball_Stone",
|
||||
u8"P_Ball_Wood",
|
||||
u8"P_Box",
|
||||
u8"P_Dome",
|
||||
u8"P_Modul_01",
|
||||
u8"P_Modul_03",
|
||||
u8"P_Modul_08",
|
||||
u8"P_Modul_17",
|
||||
u8"P_Modul_18",
|
||||
u8"P_Modul_19",
|
||||
u8"P_Modul_25",
|
||||
u8"P_Modul_26",
|
||||
u8"P_Modul_29",
|
||||
u8"P_Modul_30",
|
||||
u8"P_Modul_34",
|
||||
u8"P_Modul_37",
|
||||
u8"P_Modul_41",
|
||||
u8"PS_Levelstart",
|
||||
u8"PE_Levelende",
|
||||
u8"PC_Checkpoints",
|
||||
u8"PR_Resetpoints",
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
} // namespace GroupNames
|
||||
|
||||
namespace TextureNames {
|
||||
// clang-format off
|
||||
constexpr char8_t RAIL_ENVIRONMENT[] = u8"Rail_Environment.bmp";
|
||||
// clang-format on
|
||||
|
||||
} // namespace TextureNames
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Check Functions
|
||||
|
||||
/**
|
||||
* @brief Check whether given 2 float point values are equal with given tolerance.
|
||||
* @param lhs
|
||||
* @param rhs
|
||||
* @param tolerance
|
||||
* @return
|
||||
*/
|
||||
bool FPEqual(L::CKFLOAT lhs, L::CKFLOAT rhs, L::CKFLOAT tolerance);
|
||||
/**
|
||||
* @brief
|
||||
* @param[in] ctx Can not be nullptr.
|
||||
* @param[in] name Can not be nullptr.
|
||||
* @return Found pointer to CKGroup, otherwise nullptr.
|
||||
*/
|
||||
O::CKGroup* FetchGroup(C::CKContext* ctx, L::CKSTRING name);
|
||||
std::vector<O::CK3dObject*> FetchPhysicalized3dObjects(C::CKContext* ctx);
|
||||
/**
|
||||
* @brief Check whether given CKTexture has the given file name (case-insensitive).
|
||||
* @param[in] tex Can not be nullptr.
|
||||
* @param[in] name Can not be nullptr.
|
||||
* @return True if it is, otherwise false.
|
||||
*/
|
||||
bool CheckTextureFileName(O::CKTexture* tex, L::CKSTRING name);
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* @param[in] group Can not be nullptr.
|
||||
* @return All objects is the child class of CK3dEntity.
|
||||
*/
|
||||
std::vector<O::CK3dObject*> Iter3dObjects(O::CKGroup* group);
|
||||
/**
|
||||
* @brief
|
||||
* @param[in] mesh Can not be nullptr.
|
||||
* @return All nullptr reference are removed.
|
||||
*/
|
||||
std::vector<O::CKMaterial*> IterMaterial(O::CKMesh* mesh);
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* @param[in] obj Can not be nullptr.
|
||||
* @return
|
||||
*/
|
||||
const char8_t* RenderObjectName(O::CKObject* obj);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Rule::Shared
|
||||
@@ -1,132 +0,0 @@
|
||||
#include "YYCRules.hpp"
|
||||
#include "Shared.hpp"
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
|
||||
#pragma region YYC Rule 1
|
||||
|
||||
constexpr char8_t YYC1[] = u8"YYC1";
|
||||
|
||||
YYCRule1::YYCRule1() : IRule() {}
|
||||
|
||||
YYCRule1::~YYCRule1() {}
|
||||
|
||||
std::u8string_view YYCRule1::GetRuleName() const {
|
||||
return YYC1;
|
||||
}
|
||||
|
||||
void YYCRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
auto* ctx = level.GetCKContext();
|
||||
|
||||
// We get "Phys_FloorRails" group first.
|
||||
auto* phys_floorrails = Shared::FetchGroup(ctx, Shared::GroupNames::PHYS_FLOORRAILS);
|
||||
if (phys_floorrails == nullptr) return;
|
||||
|
||||
// Create container holding smooth meshes
|
||||
std::set<O::CKMesh*> smooth_meshes;
|
||||
// We iterate all object grouped into it.
|
||||
auto group_3dobjects = Shared::Iter3dObjects(phys_floorrails);
|
||||
for (auto* group_3dobject : group_3dobjects) {
|
||||
// Then we iterate their current meshes
|
||||
auto* mesh = group_3dobject->GetCurrentMesh();
|
||||
if (mesh == nullptr) continue;
|
||||
|
||||
// Iterate all meshes
|
||||
auto mtls = Shared::IterMaterial(mesh);
|
||||
for (auto* mtl : mtls) {
|
||||
// Check whether all texture referred by this mesh are "Rail_Environment".
|
||||
auto texture = mtl->GetTexture();
|
||||
if (texture == nullptr) continue;
|
||||
if (!Shared::CheckTextureFileName(texture, Shared::TextureNames::RAIL_ENVIRONMENT)) {
|
||||
// No, this is not rail texture, throw error.
|
||||
reporter.FormatError(
|
||||
YYC1,
|
||||
u8R"(Object "%s" is grouped into Phys_FloorRails, but its texture "%s" (referred by mesh "%s" and material "%s") seems not the rail texture. This will cause some parts of this object be smooth unexpectly.)",
|
||||
Shared::RenderObjectName(group_3dobject),
|
||||
Shared::RenderObjectName(texture),
|
||||
Shared::RenderObjectName(mesh),
|
||||
Shared::RenderObjectName(mtl));
|
||||
}
|
||||
}
|
||||
|
||||
// Record this mesh into set.
|
||||
smooth_meshes.emplace(mesh);
|
||||
}
|
||||
|
||||
// Now we make sure that these smooth mesh is not referred by any other object.
|
||||
// We iterate all 3d object first
|
||||
auto all_3dobject = level.Get3dObjects();
|
||||
for (auto* obj : all_3dobject) {
|
||||
// Then we get its current mesh
|
||||
auto* mesh = obj->GetCurrentMesh();
|
||||
if (mesh == nullptr) continue;
|
||||
|
||||
// Check whether its mesh is in smooth mesh,
|
||||
// and itself is not in "Phys_FloorRails" group
|
||||
if (!obj->IsInGroup(phys_floorrails) && smooth_meshes.contains(mesh)) {
|
||||
// Report error.
|
||||
reporter.FormatError(
|
||||
YYC1,
|
||||
u8R"(Object "%s" is not grouped into Phys_FloorRails, but some objects grouped into Phys_FloorRails refer its mesh "%s". This will cause this object be smooth unexpectly.)",
|
||||
Shared::RenderObjectName(obj),
|
||||
Shared::RenderObjectName(mesh));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region YYC Rule 2
|
||||
|
||||
constexpr char8_t YYC2[] = u8"YYC2";
|
||||
|
||||
YYCRule2::YYCRule2() : IRule() {}
|
||||
|
||||
YYCRule2::~YYCRule2() {}
|
||||
|
||||
std::u8string_view YYCRule2::GetRuleName() const {
|
||||
return YYC2;
|
||||
}
|
||||
|
||||
void YYCRule2::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
auto* ctx = level.GetCKContext();
|
||||
auto physicalized_3dobjects = Shared::FetchPhysicalized3dObjects(ctx);
|
||||
|
||||
// Iterate all physicalized 3dobject
|
||||
for (auto* physicalized_3dobject : physicalized_3dobjects) {
|
||||
// Get its mesh
|
||||
auto* mesh = physicalized_3dobject->GetCurrentMesh();
|
||||
if (mesh == nullptr) continue;
|
||||
|
||||
// Create a bool vector with vertex count and false init value.
|
||||
std::vector<bool> used_vertex(mesh->GetVertexCount(), false);
|
||||
// Iterate all face and set their vertex as used.
|
||||
auto* face_indices = mesh->GetFaceIndices();
|
||||
for (L::CKDWORD face_idx = 0; face_idx < mesh->GetFaceCount(); ++face_idx) {
|
||||
used_vertex[face_indices[face_idx * 3]] = true;
|
||||
used_vertex[face_indices[face_idx * 3 + 1]] = true;
|
||||
used_vertex[face_indices[face_idx * 3 + 2]] = true;
|
||||
}
|
||||
// Check whether there is unused vertex
|
||||
auto has_unused_vertex = std::any_of(used_vertex.begin(), used_vertex.end(), [](bool v) { return v == false; });
|
||||
// If there is unused vertex, report error
|
||||
if (has_unused_vertex) {
|
||||
reporter.FormatError(
|
||||
YYC2,
|
||||
u8R"(Object "%s" is grouped into physicalization groups, and its referred mesh "%s" has isolated vertex. This will cause it can not be physicalized.)",
|
||||
Shared::RenderObjectName(physicalized_3dobject),
|
||||
Shared::RenderObjectName(mesh));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Rule
|
||||
@@ -1,40 +0,0 @@
|
||||
#pragma once
|
||||
#include "../Rule.hpp"
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
|
||||
/**
|
||||
* @brief YYC12345 Rule 1
|
||||
* @details
|
||||
* The object grouped into "Phys_FloorRails" should only be rails, otherwise their meshes' UV will be smooth.
|
||||
* Additionally, these smooth UV meshes will also affect those objects refering them.
|
||||
*/
|
||||
class YYCRule1 : public IRule {
|
||||
public:
|
||||
YYCRule1();
|
||||
virtual ~YYCRule1();
|
||||
YYCC_DELETE_COPY_MOVE(YYCRule1)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief YYC12345 Rule 2
|
||||
* @details
|
||||
* The object grouped into physicalization group should not have isolated vertex,
|
||||
* otherwise it will fail to be physicalized.
|
||||
*/
|
||||
class YYCRule2 : public IRule {
|
||||
public:
|
||||
YYCRule2();
|
||||
virtual ~YYCRule2();
|
||||
YYCC_DELETE_COPY_MOVE(YYCRule2)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
#include "../Rule.hpp"
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
|
||||
}
|
||||
64
Ballance/BMapInspector/Ruleset/BBugRules.cpp
Normal file
64
Ballance/BMapInspector/Ruleset/BBugRules.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "BBugRules.hpp"
|
||||
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
#pragma region BBug Rule 1
|
||||
|
||||
BBugRule1::BBugRule1() : Rule::IRule() {}
|
||||
|
||||
BBugRule1::~BBugRule1() {}
|
||||
|
||||
std::u8string_view BBugRule1::GetRuleName() const {
|
||||
return u8"BBUG1";
|
||||
}
|
||||
|
||||
void BBugRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
if (!level.GetTargetLights().empty()) {
|
||||
reporter.WriteInfo(u8"Using light in map is not suggested.");
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region BBug Rule 2
|
||||
|
||||
BBugRule2::BBugRule2() : Rule::IRule() {}
|
||||
|
||||
BBugRule2::~BBugRule2() {}
|
||||
|
||||
std::u8string_view BBugRule2::GetRuleName() const {
|
||||
return u8"BBUG2";
|
||||
}
|
||||
|
||||
void BBugRule2::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
if (!level.GetTargetCameras().empty()) {
|
||||
reporter.WriteInfo(u8"Using camera in map is not suggested.");
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region BBug Rule 3
|
||||
|
||||
BBugRule3::BBugRule3() : Rule::IRule() {}
|
||||
|
||||
BBugRule3::~BBugRule3() {}
|
||||
|
||||
std::u8string_view BBugRule3::GetRuleName() const {
|
||||
return u8"BBUG3";
|
||||
}
|
||||
|
||||
void BBugRule3::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
// TODO:
|
||||
// This rule is complex and can be done by Ballance Blender Plugin.
|
||||
// So we are not urgently to implement it in there.
|
||||
// Just make a rule placeholder in there and may finish it in future.
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Ruleset
|
||||
54
Ballance/BMapInspector/Ruleset/BBugRules.hpp
Normal file
54
Ballance/BMapInspector/Ruleset/BBugRules.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
#include "../Rule.hpp"
|
||||
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
/**
|
||||
* @brief BBug Rule 1
|
||||
* @details
|
||||
* Using light in map is not suggested.
|
||||
*/
|
||||
class BBugRule1 : public Rule::IRule {
|
||||
public:
|
||||
BBugRule1();
|
||||
virtual ~BBugRule1();
|
||||
YYCC_DELETE_COPY_MOVE(BBugRule1)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief BBug Rule 2
|
||||
* @details
|
||||
* Using camera in map is not suggested.
|
||||
*/
|
||||
class BBugRule2 : public Rule::IRule {
|
||||
public:
|
||||
BBugRule2();
|
||||
virtual ~BBugRule2();
|
||||
YYCC_DELETE_COPY_MOVE(BBugRule2)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief BBug Rule 3
|
||||
* @details
|
||||
* Check whether the parameters of all materials is same with Ballance vanilla settings.
|
||||
*/
|
||||
class BBugRule3 : public Rule::IRule {
|
||||
public:
|
||||
BBugRule3();
|
||||
virtual ~BBugRule3();
|
||||
YYCC_DELETE_COPY_MOVE(BBugRule3)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
}
|
||||
80
Ballance/BMapInspector/Ruleset/ChirsRules.cpp
Normal file
80
Ballance/BMapInspector/Ruleset/ChirsRules.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "ChirsRules.hpp"
|
||||
#include "Shared/Utility.hpp"
|
||||
#include "Shared/Name.hpp"
|
||||
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
#pragma region Chirs Rule 1
|
||||
|
||||
// Reference: https://tieba.baidu.com/p/5913556704
|
||||
|
||||
ChirsRule1::ChirsRule1() : Rule::IRule() {}
|
||||
|
||||
ChirsRule1::~ChirsRule1() {}
|
||||
|
||||
std::u8string_view ChirsRule1::GetRuleName() const {
|
||||
return u8"CHIRS1";
|
||||
}
|
||||
|
||||
void ChirsRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
constexpr char8_t MTL_LATERNE_VERLAUF[] = u8"Laterne_Verlauf";
|
||||
auto* ctx = level.GetCKContext();
|
||||
|
||||
// Fetch Laterne_Verlauf first
|
||||
auto* latern = Shared::Utility::FetchMaterial(ctx, MTL_LATERNE_VERLAUF);
|
||||
if (latern == nullptr) return;
|
||||
|
||||
// Report warning if this material's texture is not Laterne_Verlauf.tga
|
||||
auto* latern_tex = latern->GetTexture();
|
||||
if (latern_tex == nullptr) {
|
||||
reporter.FormatWarning(
|
||||
u8"Find a material named %s but it doesn't have associated texture. "
|
||||
u8"It occupies the magic material %s which affect the ray of latern in game. Please confirm this is your intention.",
|
||||
Shared::Utility::QuoteText(MTL_LATERNE_VERLAUF).c_str(),
|
||||
Shared::Utility::QuoteText(MTL_LATERNE_VERLAUF).c_str());
|
||||
} else {
|
||||
if (!Shared::Utility::CheckTextureFileName(latern_tex, Shared::Name::Texture::LATERNE_VERLAUF)) {
|
||||
reporter.FormatWarning(
|
||||
u8"Find a material named %s but its texture is not %s. "
|
||||
u8"It occupies the magic material %s which affect the ray of latern in game. Please confirm this is your intention.",
|
||||
Shared::Utility::QuoteText(MTL_LATERNE_VERLAUF).c_str(),
|
||||
Shared::Utility::QuoteText(Shared::Name::Texture::LATERNE_VERLAUF).c_str(),
|
||||
Shared::Utility::QuoteText(MTL_LATERNE_VERLAUF).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Report warning if there is multiple Laterne_Verlauf material.
|
||||
auto next_latern = ctx->GetObjectByNameAndClass(MTL_LATERNE_VERLAUF, C::CK_CLASSID::CKCID_MATERIAL, latern);
|
||||
if (next_latern != nullptr) {
|
||||
reporter.FormatWarning(u8"There are multiple materials named %s. This will cause the disappearance of some latern's rays.",
|
||||
Shared::Utility::QuoteText(MTL_LATERNE_VERLAUF).c_str());
|
||||
}
|
||||
|
||||
// Report warning if some materials' texture is Laterne_Verlauf,
|
||||
// but its name is not Laterne_Verlauf.
|
||||
for (auto* other_mtl : level.GetMaterials()) {
|
||||
if (C::CKStrEqual(other_mtl->GetName(), MTL_LATERNE_VERLAUF)) continue;
|
||||
|
||||
auto other_mtl_tex = other_mtl->GetTexture();
|
||||
if (other_mtl_tex == nullptr) continue;
|
||||
|
||||
if (Shared::Utility::CheckTextureFileName(other_mtl_tex, Shared::Name::Texture::LATERNE_VERLAUF)) {
|
||||
reporter.FormatWarning(
|
||||
u8"Find material %s referring texture %s, but its name is not %s. "
|
||||
u8"Please confirm the usage of this material. If it is used as %s, please rename it into %s to have correct latern ray.",
|
||||
Shared::Utility::QuoteObjectName(other_mtl).c_str(),
|
||||
Shared::Utility::QuoteText(Shared::Name::Texture::LATERNE_VERLAUF).c_str(),
|
||||
Shared::Utility::QuoteText(MTL_LATERNE_VERLAUF).c_str(),
|
||||
Shared::Utility::QuoteText(MTL_LATERNE_VERLAUF).c_str(),
|
||||
Shared::Utility::QuoteText(MTL_LATERNE_VERLAUF).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Ruleset
|
||||
@@ -1,21 +1,19 @@
|
||||
#pragma once
|
||||
#include "../Rule.hpp"
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
|
||||
// Reference: https://tieba.baidu.com/p/5913556704
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
/**
|
||||
* @brief Chirs241097 Rule 1
|
||||
* @brief chirs241097 Rule 1
|
||||
* @details
|
||||
* This rule will make sure that there is only 1 texture named Laterne_Verlauf in map,
|
||||
* which represent the ray of latern.
|
||||
*/
|
||||
class Chirs1Rule : public IRule {
|
||||
class ChirsRule1 : public Rule::IRule {
|
||||
public:
|
||||
Chirs1Rule();
|
||||
virtual ~Chirs1Rule();
|
||||
YYCC_DELETE_COPY_MOVE(Chirs1Rule)
|
||||
ChirsRule1();
|
||||
virtual ~ChirsRule1();
|
||||
YYCC_DELETE_COPY_MOVE(ChirsRule1)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
139
Ballance/BMapInspector/Ruleset/GpRules.cpp
Normal file
139
Ballance/BMapInspector/Ruleset/GpRules.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
#include "GpRules.hpp"
|
||||
#include "Shared/Utility.hpp"
|
||||
#include "Shared/Sector.hpp"
|
||||
#include <set>
|
||||
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
#pragma region GP1 Rule
|
||||
|
||||
// Reference: https://tieba.baidu.com/p/3182981807
|
||||
|
||||
GpRule1::GpRule1() : Rule::IRule() {}
|
||||
|
||||
GpRule1::~GpRule1() {}
|
||||
|
||||
std::u8string_view GpRule1::GetRuleName() const {
|
||||
return u8"GP1";
|
||||
}
|
||||
|
||||
void GpRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
// TODO:
|
||||
// This rule is complex and can be done by Ballance Blender Plugin.
|
||||
// So we are not urgently to implement it in there.
|
||||
// Just make a rule placeholder in there and may finish it in future.
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region GP2 Rule
|
||||
|
||||
GpRule2::GpRule2() : Rule::IRule() {}
|
||||
|
||||
GpRule2::~GpRule2() {}
|
||||
|
||||
std::u8string_view GpRule2::GetRuleName() const {
|
||||
return u8"GP2";
|
||||
}
|
||||
|
||||
void GpRule2::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
auto* ctx = level.GetCKContext();
|
||||
Shared::Sector::SectorNameBuilder builder;
|
||||
|
||||
// We need collect all group names first,
|
||||
// becuase in following code we need frequent visit them
|
||||
std::set<std::u8string> group_names;
|
||||
for (auto* group : level.GetGroups()) {
|
||||
auto group_name = group->GetName();
|
||||
if (group_name != nullptr) {
|
||||
group_names.emplace(std::u8string(group_name));
|
||||
}
|
||||
}
|
||||
|
||||
// Check the sector count of this game.
|
||||
L::CKDWORD sector_count;
|
||||
for (sector_count = Shared::Sector::MIN_SECTOR; sector_count <= Shared::Sector::MAX_SECTOR; ++sector_count) {
|
||||
// Build name first with special treat for sector 9
|
||||
if (sector_count != 9) {
|
||||
auto sector_name = builder.get_name(sector_count);
|
||||
if (!group_names.contains(sector_name)) {
|
||||
if (sector_count == Shared::Sector::MIN_SECTOR) {
|
||||
reporter.WriteError(u8"Can not find any reasonable sector group in your map.");
|
||||
return;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
auto sector_names = builder.get_sector9_names();
|
||||
bool has_legacy_sector = group_names.contains(sector_names.legacy_name);
|
||||
bool has_intuitive_sector = group_names.contains(sector_names.intuitive_name);
|
||||
if (!has_legacy_sector && !has_intuitive_sector) {
|
||||
break;
|
||||
}
|
||||
if (has_legacy_sector && has_intuitive_sector) {
|
||||
reporter.FormatError(u8"Found %s and %s at the same map. This is not allowed.",
|
||||
Shared::Utility::QuoteText(sector_names.legacy_name).c_str(),
|
||||
Shared::Utility::QuoteText(sector_names.intuitive_name).c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now sector_count is the first sector which can not find,
|
||||
// so we need minus one on it.
|
||||
--sector_count;
|
||||
|
||||
// Report sector count info.
|
||||
reporter.FormatInfo(u8"Your map has %" PRIuCKDWORD " sector(s).", sector_count);
|
||||
// Report special warning for map which only contain 1 sector.
|
||||
if (sector_count == 1) {
|
||||
reporter.WriteWarning(u8"Your map only have one sector. "
|
||||
u8"This is okey but not suggested because it will cause mosaic issue on the flames of checkpoint. "
|
||||
u8"Consider adding another sector to resolve this issue.");
|
||||
}
|
||||
// Report warning for sector greater than 8.
|
||||
if (sector_count > 8) {
|
||||
reporter.WriteWarning(u8"You are creating a map with more than 8 sectors. "
|
||||
u8"This will cause vanilla Ballance freezed when loading it. "
|
||||
u8"Please make sure that all players of your map have properly set 999 sector loader up.");
|
||||
}
|
||||
// If there is sector 9, check its kind and report wanring if it is intuitive kind.
|
||||
if (sector_count > 8) {
|
||||
auto sector_names = builder.get_sector9_names();
|
||||
if (group_names.contains(sector_names.intuitive_name)) {
|
||||
reporter.FormatWarning(u8"You are using intuitive sector name, %s, for sector 9. "
|
||||
u8"This is only accepted by new 999 sector loader.",
|
||||
Shared::Utility::QuoteText(sector_names.intuitive_name).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// We continue check following sectors to make sure that all sector is successive.
|
||||
for (L::CKDWORD i = sector_count + 1; i <= Shared::Sector::MAX_SECTOR; ++i) {
|
||||
if (i != 9) {
|
||||
auto sector_name = builder.get_name(i);
|
||||
if (group_names.contains(sector_name)) {
|
||||
reporter.FormatError(u8"Found group %s unexpected. "
|
||||
u8"Please check whether sector groups are successive in your map.",
|
||||
Shared::Utility::QuoteText(sector_name).c_str());
|
||||
}
|
||||
} else {
|
||||
auto sector_names = builder.get_sector9_names();
|
||||
bool has_legacy_sector = group_names.contains(sector_names.legacy_name);
|
||||
bool has_intuitive_sector = group_names.contains(sector_names.intuitive_name);
|
||||
if (has_legacy_sector || has_intuitive_sector) {
|
||||
reporter.FormatError(u8"Found group %s or %s unexpected. "
|
||||
u8"Please check whether sector groups are successive in your map.",
|
||||
Shared::Utility::QuoteText(sector_names.legacy_name).c_str(),
|
||||
Shared::Utility::QuoteText(sector_names.intuitive_name).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Ruleset
|
||||
44
Ballance/BMapInspector/Ruleset/GpRules.hpp
Normal file
44
Ballance/BMapInspector/Ruleset/GpRules.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
#include "../Rule.hpp"
|
||||
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
/**
|
||||
* @brief Gamepiaynmo Rule 1
|
||||
* @details
|
||||
* The most comprehensive group checker inspired from Ballance Blender Plugin.
|
||||
*/
|
||||
class GpRule1 : public Rule::IRule {
|
||||
public:
|
||||
GpRule1();
|
||||
virtual ~GpRule1();
|
||||
YYCC_DELETE_COPY_MOVE(GpRule1)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Gamepiaynmo Rule 2
|
||||
* @details
|
||||
* This rule will:
|
||||
* \li Show how many sector located in given map.
|
||||
* \li Check whether there is sector group.
|
||||
* \li Check whether use intuitive sector name for sector 9.
|
||||
* \li Warn for sector count is equal to 1. It will cause mosaic issue on the flames of checkpoint.
|
||||
* \li Warn for sector count greater than 8. It will cause vanilla game freezed without 999 sector loader.
|
||||
* \li Check whether sector group is successive.
|
||||
*/
|
||||
class GpRule2 : public Rule::IRule {
|
||||
public:
|
||||
GpRule2();
|
||||
virtual ~GpRule2();
|
||||
YYCC_DELETE_COPY_MOVE(GpRule2)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,23 +1,22 @@
|
||||
#include "LXRules.hpp"
|
||||
#include "Shared.hpp"
|
||||
#include "Shared/Utility.hpp"
|
||||
#include "Shared/Name.hpp"
|
||||
#include <set>
|
||||
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
#pragma region LX Rule 1
|
||||
|
||||
constexpr char8_t LX1[] = u8"LX1";
|
||||
|
||||
LXRule1::LXRule1() : IRule() {}
|
||||
LXRule1::LXRule1() : Rule::IRule() {}
|
||||
|
||||
LXRule1::~LXRule1() {}
|
||||
|
||||
std::u8string_view LXRule1::GetRuleName() const {
|
||||
return LX1;
|
||||
return u8"LX1";
|
||||
}
|
||||
|
||||
void LXRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
@@ -25,10 +24,10 @@ namespace BMapInspector::Rule {
|
||||
|
||||
// First we fetch all Ballance element and push them into set.
|
||||
std::set<O::CK3dObject*> elements;
|
||||
for (auto* group_name : Shared::GroupNames::ALL_PH) {
|
||||
auto* group = Shared::FetchGroup(ctx, group_name);
|
||||
for (auto* group_name : Shared::Name::Group::ALL_PH) {
|
||||
auto* group = Shared::Utility::FetchGroup(ctx, group_name);
|
||||
if (group == nullptr) continue;
|
||||
auto group_objects = Shared::Iter3dObjects(group);
|
||||
auto group_objects = Shared::Utility::Iter3dObjects(group);
|
||||
for (auto* group_object : group_objects) {
|
||||
elements.emplace(group_object);
|
||||
}
|
||||
@@ -49,7 +48,7 @@ namespace BMapInspector::Rule {
|
||||
// because we do not want to duplicatedly process it.
|
||||
if (mesh_insert_rv.second) {
|
||||
// Iterate all meshes
|
||||
auto mtls = Shared::IterMaterial(mesh);
|
||||
auto mtls = Shared::Utility::IterMaterial(mesh);
|
||||
for (auto* mtl : mtls) {
|
||||
// Add into material set
|
||||
auto mtl_insert_rv = element_materials.emplace(mtl);
|
||||
@@ -76,23 +75,21 @@ namespace BMapInspector::Rule {
|
||||
if (mesh == nullptr) continue;
|
||||
// And check mesh
|
||||
if (element_meshes.contains(mesh)) {
|
||||
reporter.FormatError(
|
||||
LX1,
|
||||
u8R"(Object "%s" used mesh "%s" is already used by a Ballance element. This will cause this object can not be rendered correctly in level.)",
|
||||
Shared::RenderObjectName(other_object),
|
||||
Shared::RenderObjectName(mesh));
|
||||
reporter.FormatError(u8"Object %s used mesh %s is already used by a Ballance element. "
|
||||
u8"This will cause this object can not be rendered correctly in level.",
|
||||
Shared::Utility::QuoteObjectName(other_object).c_str(),
|
||||
Shared::Utility::QuoteObjectName(mesh).c_str());
|
||||
} else {
|
||||
// If not, check material.
|
||||
// Iterate all meshes
|
||||
auto mtls = Shared::IterMaterial(mesh);
|
||||
auto mtls = Shared::Utility::IterMaterial(mesh);
|
||||
for (auto* mtl : mtls) {
|
||||
if (element_materials.contains(mtl)) {
|
||||
reporter.FormatError(
|
||||
LX1,
|
||||
u8R"(Object "%s" used material "%s" (referred by mesh "%s") is already used by a Ballance element. This will cause this object can not be rendered correctly in level.)",
|
||||
Shared::RenderObjectName(other_object),
|
||||
Shared::RenderObjectName(mtl),
|
||||
Shared::RenderObjectName(mesh));
|
||||
reporter.FormatError(u8"Object %s used material %s (referred by mesh %s) is already used by a Ballance element. "
|
||||
u8"This will cause this object can not be rendered correctly in level.",
|
||||
Shared::Utility::QuoteObjectName(other_object).c_str(),
|
||||
Shared::Utility::QuoteObjectName(mtl).c_str(),
|
||||
Shared::Utility::QuoteObjectName(mesh).c_str());
|
||||
} else {
|
||||
// Still not, check texture.
|
||||
// Fetch texture
|
||||
@@ -101,12 +98,12 @@ namespace BMapInspector::Rule {
|
||||
// And check it
|
||||
if (element_textures.contains(texture)) {
|
||||
reporter.FormatError(
|
||||
LX1,
|
||||
u8R"(Object "%s" used texture "%s" (referred by mesh "%s" and material "%s") is already used by a Ballance element. This will cause this object can not be rendered correctly in level.)",
|
||||
Shared::RenderObjectName(other_object),
|
||||
Shared::RenderObjectName(texture),
|
||||
Shared::RenderObjectName(mesh),
|
||||
Shared::RenderObjectName(mtl));
|
||||
u8"Object %s used texture %s (referred by mesh %s and material %s) is already used by a Ballance element. "
|
||||
u8"This will cause this object can not be rendered correctly in level.",
|
||||
Shared::Utility::QuoteObjectName(other_object).c_str(),
|
||||
Shared::Utility::QuoteObjectName(texture).c_str(),
|
||||
Shared::Utility::QuoteObjectName(mesh).c_str(),
|
||||
Shared::Utility::QuoteObjectName(mtl).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,4 +113,4 @@ namespace BMapInspector::Rule {
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Rule
|
||||
} // namespace BMapInspector::Ruleset
|
||||
@@ -1,14 +1,14 @@
|
||||
#pragma once
|
||||
#include "../Rule.hpp"
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
/**
|
||||
* @brief LengXi Rule 1
|
||||
* @details
|
||||
* All meshes, materials and textures used by Ballance elements should not be used by any other objects.
|
||||
*/
|
||||
class LXRule1 : public IRule {
|
||||
class LXRule1 : public Rule::IRule {
|
||||
public:
|
||||
LXRule1();
|
||||
virtual ~LXRule1();
|
||||
36
Ballance/BMapInspector/Ruleset/SOneRules.cpp
Normal file
36
Ballance/BMapInspector/Ruleset/SOneRules.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "SOneRules.hpp"
|
||||
#include "Shared/Utility.hpp"
|
||||
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
#pragma region SOne Rule 1
|
||||
|
||||
SOneRule1::SOneRule1() : Rule::IRule() {}
|
||||
|
||||
SOneRule1::~SOneRule1() {}
|
||||
|
||||
std::u8string_view SOneRule1::GetRuleName() const {
|
||||
return u8"SONE1";
|
||||
}
|
||||
|
||||
void SOneRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
auto* ctx = level.GetCKContext();
|
||||
auto physicalized_3dobjects = Shared::Utility::FetchPhysicalized3dObjects(ctx);
|
||||
|
||||
for (auto* physicalized_3dobject : physicalized_3dobjects) {
|
||||
auto* mesh = physicalized_3dobject->GetCurrentMesh();
|
||||
if (mesh == nullptr) {
|
||||
reporter.FormatError(u8"Object %s is grouped into physicalization group, but it doesn't have any associated mesh. "
|
||||
u8"This will cause itself and following objects can not be physicalized.",
|
||||
Shared::Utility::QuoteObjectName(physicalized_3dobject).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Ruleset
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
#include "../Rule.hpp"
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
/**
|
||||
* @brief SomeOne_001 Rule 1
|
||||
@@ -9,7 +9,7 @@ namespace BMapInspector::Rule {
|
||||
* If there is a physicalized object without any mesh,
|
||||
* itself and following objects will not be physicalized.
|
||||
*/
|
||||
class SOneRule1 : public IRule {
|
||||
class SOneRule1 : public Rule::IRule {
|
||||
public:
|
||||
SOneRule1();
|
||||
virtual ~SOneRule1();
|
||||
@@ -20,4 +20,4 @@ namespace BMapInspector::Rule {
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
} // namespace BMapInspector::Rule
|
||||
} // namespace BMapInspector::Ruleset
|
||||
@@ -1,28 +1,28 @@
|
||||
#include "SSBRules.hpp"
|
||||
#include "Shared.hpp"
|
||||
#include "Shared/Utility.hpp"
|
||||
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace V = LibCmo::VxMath;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
#pragma region SSB Rule 1
|
||||
|
||||
constexpr char8_t SSB1[] = u8"SSB1";
|
||||
constexpr L::CKFLOAT TOLERANCE = 0.001f;
|
||||
|
||||
SSBRule1::SSBRule1() : IRule() {}
|
||||
SSBRule1::SSBRule1() : Rule::IRule() {}
|
||||
|
||||
SSBRule1::~SSBRule1() {}
|
||||
|
||||
std::u8string_view SSBRule1::GetRuleName() const {
|
||||
return SSB1;
|
||||
return u8"SSB1";
|
||||
}
|
||||
|
||||
void SSBRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
auto* ctx = level.GetCKContext();
|
||||
auto physicalized_3dobjects = Shared::FetchPhysicalized3dObjects(ctx);
|
||||
auto physicalized_3dobjects = Shared::Utility::FetchPhysicalized3dObjects(ctx);
|
||||
|
||||
// Iterate all physicalized 3dobject
|
||||
for (auto* physicalized_3dobject : physicalized_3dobjects) {
|
||||
@@ -34,26 +34,25 @@ namespace BMapInspector::Rule {
|
||||
// because it rely on some premise.
|
||||
// But it is simple, especially we do not have fully implement VxMatrix,
|
||||
// or have any linear algebra library.
|
||||
|
||||
|
||||
// Extract 3 columns
|
||||
V::VxVector3 col1(matrix[0][0], matrix[1][0], matrix[2][0]);
|
||||
V::VxVector3 col2(matrix[0][1], matrix[1][1], matrix[2][1]);
|
||||
V::VxVector3 col3(matrix[0][2], matrix[1][2], matrix[2][2]);
|
||||
// Compute their length, then check their value with tolerance.
|
||||
bool has_scale = false;
|
||||
if (!Shared::FPEqual(col1.Length(), 1.0f, TOLERANCE)) has_scale = true;
|
||||
if (!Shared::FPEqual(col2.Length(), 1.0f, TOLERANCE)) has_scale = true;
|
||||
if (!Shared::FPEqual(col3.Length(), 1.0f, TOLERANCE)) has_scale = true;
|
||||
if (!Shared::Utility::FPEqual(col1.Length(), 1.0f, TOLERANCE)) has_scale = true;
|
||||
if (!Shared::Utility::FPEqual(col2.Length(), 1.0f, TOLERANCE)) has_scale = true;
|
||||
if (!Shared::Utility::FPEqual(col3.Length(), 1.0f, TOLERANCE)) has_scale = true;
|
||||
// If it has scale factor, report error
|
||||
if (has_scale) {
|
||||
reporter.FormatError(
|
||||
SSB1,
|
||||
u8R"(Object "%s" grouped into physicalization groups has scale factor. This will cause its collision shape is different with its render shape.)",
|
||||
Shared::RenderObjectName(physicalized_3dobject));
|
||||
reporter.FormatError(u8"Object %s grouped into physicalization groups has scale factor. "
|
||||
u8"This will cause its collision shape is different with its render shape.",
|
||||
Shared::Utility::QuoteObjectName(physicalized_3dobject).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Rule
|
||||
} // namespace BMapInspector::Ruleset
|
||||
@@ -1,14 +1,14 @@
|
||||
#pragma once
|
||||
#include "../Rule.hpp"
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
/**
|
||||
* @brief speedystoneball Rule 1
|
||||
* @details
|
||||
* Pjysicalized object should not have scale factor, especially negative scale factor (mirror).
|
||||
* Physicalized object should not have scale factor, especially negative scale factor (mirror).
|
||||
*/
|
||||
class SSBRule1 : public IRule {
|
||||
class SSBRule1 : public Rule::IRule {
|
||||
public:
|
||||
SSBRule1();
|
||||
virtual ~SSBRule1();
|
||||
@@ -19,4 +19,4 @@ namespace BMapInspector::Rule {
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
} // namespace BMapInspector::Rule
|
||||
} // namespace BMapInspector::Ruleset
|
||||
318
Ballance/BMapInspector/Ruleset/Shared/DupCmp.cpp
Normal file
318
Ballance/BMapInspector/Ruleset/Shared/DupCmp.cpp
Normal file
@@ -0,0 +1,318 @@
|
||||
#include "DupCmp.hpp"
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/string/op.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace V = LibCmo::VxMath;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
namespace strop = yycc::string::op;
|
||||
|
||||
namespace std {
|
||||
|
||||
#pragma region Primitive CK Hasher
|
||||
|
||||
using BMapInspector::Ruleset::Shared::DupCmp::Hasher;
|
||||
|
||||
template<>
|
||||
struct hash<V::VxColor> {
|
||||
[[nodiscard]] size_t operator()(const V::VxColor& color) const noexcept {
|
||||
Hasher combiner;
|
||||
combiner.update(color.r);
|
||||
combiner.update(color.g);
|
||||
combiner.update(color.b);
|
||||
combiner.update(color.a);
|
||||
return combiner.finish();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<V::VxVector2> {
|
||||
[[nodiscard]] size_t operator()(const V::VxVector2& vec) const noexcept {
|
||||
Hasher combiner;
|
||||
combiner.update(vec.x);
|
||||
combiner.update(vec.y);
|
||||
return combiner.finish();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<V::VxVector3> {
|
||||
[[nodiscard]] size_t operator()(const V::VxVector3& vec) const noexcept {
|
||||
Hasher combiner;
|
||||
combiner.update(vec.x);
|
||||
combiner.update(vec.y);
|
||||
combiner.update(vec.z);
|
||||
return combiner.finish();
|
||||
}
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace std
|
||||
|
||||
namespace BMapInspector::Ruleset::Shared::DupCmp {
|
||||
|
||||
#pragma region Hash Combiner
|
||||
|
||||
Hasher::Hasher() : seed(FNV_OFFSET_BASIS) {}
|
||||
|
||||
Hasher::~Hasher() {}
|
||||
|
||||
void Hasher::combine(ValueType h) {
|
||||
this->seed ^= h;
|
||||
this->seed *= FNV_PRIME;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region CKObject Hash and Equal
|
||||
|
||||
size_t CKTextureHash::operator()(const O::CKTexture* tex) const noexcept {
|
||||
const auto& texdata = tex->GetUnderlyingData();
|
||||
Hasher combiner;
|
||||
|
||||
auto filename = texdata.GetSlotFileName(0);
|
||||
if (filename == nullptr) {
|
||||
combiner.update(nullptr);
|
||||
} else {
|
||||
auto lower_filename = strop::to_lower(filename);
|
||||
combiner.update(lower_filename);
|
||||
}
|
||||
combiner.update(texdata.GetSaveOptions());
|
||||
combiner.update(tex->GetVideoFormat());
|
||||
|
||||
return combiner.finish();
|
||||
}
|
||||
|
||||
size_t CKMaterialHash::operator()(const O::CKMaterial* mtl) const noexcept {
|
||||
Hasher combiner;
|
||||
|
||||
combiner.update(mtl->GetDiffuse());
|
||||
combiner.update(mtl->GetAmbient());
|
||||
combiner.update(mtl->GetSpecular());
|
||||
combiner.update(mtl->GetEmissive());
|
||||
combiner.update(mtl->GetSpecularPower());
|
||||
|
||||
// TODO:
|
||||
// Use raw pointer for hash is dangerous.
|
||||
// But who cares? I simply assume that there is no memory reallocation.
|
||||
combiner.update(mtl->GetTexture());
|
||||
combiner.update(mtl->GetTextureBorderColor());
|
||||
|
||||
combiner.update(mtl->GetTextureBlendMode());
|
||||
combiner.update(mtl->GetTextureMinMode());
|
||||
combiner.update(mtl->GetTextureMagMode());
|
||||
combiner.update(mtl->GetTextureAddressMode());
|
||||
|
||||
combiner.update(mtl->GetSourceBlend());
|
||||
combiner.update(mtl->GetDestBlend());
|
||||
combiner.update(mtl->GetFillMode());
|
||||
combiner.update(mtl->GetShadeMode());
|
||||
|
||||
// TODO:
|
||||
// We also need use these "Enabled" variable to switch on/off
|
||||
// for some field's hashing according to the Virtools layout.
|
||||
// But I am lazy now.
|
||||
// I guess there is the same default values for those fields
|
||||
// controlled by some disable "Enabled" variable.
|
||||
combiner.update(mtl->GetAlphaTestEnabled());
|
||||
combiner.update(mtl->GetAlphaBlendEnabled());
|
||||
combiner.update(mtl->GetPerspectiveCorrectionEnabled());
|
||||
combiner.update(mtl->GetZWriteEnabled());
|
||||
combiner.update(mtl->GetTwoSidedEnabled());
|
||||
|
||||
combiner.update(mtl->GetAlphaRef());
|
||||
combiner.update(mtl->GetAlphaFunc());
|
||||
combiner.update(mtl->GetZFunc());
|
||||
|
||||
return combiner.finish();
|
||||
}
|
||||
|
||||
size_t CKMeshHash::operator()(const O::CKMesh* _mesh) const noexcept {
|
||||
O::CKMesh* mesh = const_cast<O::CKMesh*>(_mesh);
|
||||
Hasher combiner;
|
||||
|
||||
combiner.update(mesh->GetLitMode());
|
||||
|
||||
auto vertex_count = mesh->GetVertexCount();
|
||||
combiner.update(vertex_count);
|
||||
combiner.update_array(mesh->GetVertexPositions(), vertex_count);
|
||||
combiner.update_array(mesh->GetVertexNormals(), vertex_count);
|
||||
combiner.update_array(mesh->GetVertexUVs(), vertex_count);
|
||||
|
||||
// TODO:
|
||||
// In theory, we need remap face material slot index to underlying material CKID,
|
||||
// but its too complex. I give up.
|
||||
auto face_count = mesh->GetFaceCount();
|
||||
combiner.update(face_count);
|
||||
combiner.update_array(mesh->GetFaceIndices(), face_count * 3);
|
||||
combiner.update_array(mesh->GetFaceMaterialSlotIndexs(), face_count);
|
||||
|
||||
auto material_slot_count = mesh->GetMaterialSlotCount();
|
||||
combiner.update(material_slot_count);
|
||||
// TODO:
|
||||
// Same dangerous usage of raw pointer.
|
||||
combiner.update_array(mesh->GetMaterialSlots(), material_slot_count);
|
||||
|
||||
return combiner.finish();
|
||||
}
|
||||
|
||||
bool CKTextureEqualTo::operator()(const O::CKTexture* lhs, const O::CKTexture* rhs) const {
|
||||
// Compare underlying data
|
||||
const auto& lhs_data = lhs->GetUnderlyingData();
|
||||
const auto& rhs_data = rhs->GetUnderlyingData();
|
||||
|
||||
// Compare filename (case insensitive)
|
||||
auto lhs_filename = lhs_data.GetSlotFileName(0);
|
||||
auto rhs_filename = rhs_data.GetSlotFileName(0);
|
||||
if (!C::CKStrEqualI(lhs_filename, rhs_filename)) return false;
|
||||
|
||||
// Compare save options
|
||||
if (lhs_data.GetSaveOptions() != rhs_data.GetSaveOptions()) return false;
|
||||
// Compare video format
|
||||
if (lhs->GetVideoFormat() != rhs->GetVideoFormat()) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKMaterialEqualTo::operator()(const O::CKMaterial* lhs, const O::CKMaterial* rhs) const {
|
||||
// Compare color properties
|
||||
if (lhs->GetDiffuse() != rhs->GetDiffuse()) return false;
|
||||
if (lhs->GetAmbient() != rhs->GetAmbient()) return false;
|
||||
if (lhs->GetSpecular() != rhs->GetSpecular()) return false;
|
||||
if (lhs->GetEmissive() != rhs->GetEmissive()) return false;
|
||||
if (lhs->GetSpecularPower() != rhs->GetSpecularPower()) return false;
|
||||
|
||||
// Compare texture properties
|
||||
if (lhs->GetTexture() != rhs->GetTexture()) return false;
|
||||
if (lhs->GetTextureBorderColor() != rhs->GetTextureBorderColor()) return false;
|
||||
|
||||
// Compare texture modes
|
||||
if (lhs->GetTextureBlendMode() != rhs->GetTextureBlendMode()) return false;
|
||||
if (lhs->GetTextureMinMode() != rhs->GetTextureMinMode()) return false;
|
||||
if (lhs->GetTextureMagMode() != rhs->GetTextureMagMode()) return false;
|
||||
if (lhs->GetTextureAddressMode() != rhs->GetTextureAddressMode()) return false;
|
||||
|
||||
// Compare blend modes
|
||||
if (lhs->GetSourceBlend() != rhs->GetSourceBlend()) return false;
|
||||
if (lhs->GetDestBlend() != rhs->GetDestBlend()) return false;
|
||||
if (lhs->GetFillMode() != rhs->GetFillMode()) return false;
|
||||
if (lhs->GetShadeMode() != rhs->GetShadeMode()) return false;
|
||||
|
||||
// Compare enable flags
|
||||
if (lhs->GetAlphaTestEnabled() != rhs->GetAlphaTestEnabled()) return false;
|
||||
if (lhs->GetAlphaBlendEnabled() != rhs->GetAlphaBlendEnabled()) return false;
|
||||
if (lhs->GetPerspectiveCorrectionEnabled() != rhs->GetPerspectiveCorrectionEnabled()) return false;
|
||||
if (lhs->GetZWriteEnabled() != rhs->GetZWriteEnabled()) return false;
|
||||
if (lhs->GetTwoSidedEnabled() != rhs->GetTwoSidedEnabled()) return false;
|
||||
|
||||
// Compare alpha and z function properties
|
||||
if (lhs->GetAlphaRef() != rhs->GetAlphaRef()) return false;
|
||||
if (lhs->GetAlphaFunc() != rhs->GetAlphaFunc()) return false;
|
||||
if (lhs->GetZFunc() != rhs->GetZFunc()) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKMeshEqualTo::operator()(const O::CKMesh* _lhs, const O::CKMesh* _rhs) const {
|
||||
O::CKMesh* lhs = const_cast<O::CKMesh*>(_lhs);
|
||||
O::CKMesh* rhs = const_cast<O::CKMesh*>(_rhs);
|
||||
|
||||
// Compare lit mode
|
||||
if (lhs->GetLitMode() != rhs->GetLitMode()) return false;
|
||||
|
||||
// Compare vertex count
|
||||
auto vertex_count = lhs->GetVertexCount();
|
||||
if (vertex_count != rhs->GetVertexCount()) return false;
|
||||
|
||||
// Compare vertex data arrays
|
||||
if (!std::equal(lhs->GetVertexPositions(), lhs->GetVertexPositions() + vertex_count, rhs->GetVertexPositions())) return false;
|
||||
if (!std::equal(lhs->GetVertexNormals(), lhs->GetVertexNormals() + vertex_count, rhs->GetVertexNormals())) return false;
|
||||
if (!std::equal(lhs->GetVertexUVs(), lhs->GetVertexUVs() + vertex_count, rhs->GetVertexUVs())) return false;
|
||||
|
||||
// Compare face count
|
||||
auto face_count = lhs->GetFaceCount();
|
||||
if (face_count != rhs->GetFaceCount()) return false;
|
||||
|
||||
// Compare face data arrays
|
||||
if (!std::equal(lhs->GetFaceIndices(), lhs->GetFaceIndices() + face_count * 3, rhs->GetFaceIndices())) return false;
|
||||
if (!std::equal(lhs->GetFaceMaterialSlotIndexs(), lhs->GetFaceMaterialSlotIndexs() + face_count, rhs->GetFaceMaterialSlotIndexs()))
|
||||
return false;
|
||||
|
||||
// Compare material slot count
|
||||
auto material_slot_count = lhs->GetMaterialSlotCount();
|
||||
if (material_slot_count != rhs->GetMaterialSlotCount()) return false;
|
||||
|
||||
// Compare material slots array
|
||||
if (!std::equal(lhs->GetMaterialSlots(), lhs->GetMaterialSlots() + material_slot_count, rhs->GetMaterialSlots())) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region CKObject Wrapper
|
||||
|
||||
CKTextureWrapper::CKTextureWrapper(O::CKTexture* texture) : texture(texture), hasher(), hash(std::nullopt) {}
|
||||
|
||||
CKTextureWrapper::~CKTextureWrapper() {}
|
||||
|
||||
O::CKTexture* CKTextureWrapper::GetTexture() const { return texture; }
|
||||
|
||||
size_t CKTextureWrapper::GetHash() const {
|
||||
if (!hash.has_value()) hash = hasher(texture);
|
||||
return hash.value();
|
||||
}
|
||||
|
||||
CKMaterialWrapper::CKMaterialWrapper(O::CKMaterial* material) : material(material), hasher(), hash(std::nullopt) {}
|
||||
|
||||
CKMaterialWrapper::~CKMaterialWrapper() {}
|
||||
|
||||
O::CKMaterial* CKMaterialWrapper::GetMaterial() const { return material; }
|
||||
|
||||
size_t CKMaterialWrapper::GetHash() const {
|
||||
if (!hash.has_value()) hash = hasher(material);
|
||||
return hash.value();
|
||||
}
|
||||
|
||||
CKMeshWrapper::CKMeshWrapper(O::CKMesh* mesh) : mesh(mesh), hasher(), hash(std::nullopt) {}
|
||||
|
||||
CKMeshWrapper::~CKMeshWrapper() {}
|
||||
|
||||
O::CKMesh* CKMeshWrapper::GetMesh() const { return mesh; }
|
||||
|
||||
size_t CKMeshWrapper::GetHash() const {
|
||||
if (!hash.has_value()) hash = hasher(mesh);
|
||||
return hash.value();
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region CKObject Wrapper Hash and Equal
|
||||
|
||||
size_t CKTextureWrapperHash::operator()(const CKTextureWrapper& tex) const noexcept { return tex.GetHash(); }
|
||||
|
||||
size_t CKMaterialWrapperHash::operator()(const CKMaterialWrapper& mtl) const noexcept { return mtl.GetHash(); }
|
||||
|
||||
size_t CKMeshWrapperHash::operator()(const CKMeshWrapper& mesh) const noexcept { return mesh.GetHash(); }
|
||||
|
||||
bool CKTextureWrapperEqualTo::operator()(const CKTextureWrapper& lhs, const CKTextureWrapper& rhs) const {
|
||||
if (lhs.GetHash() != rhs.GetHash()) return false;
|
||||
return equal_to(lhs.GetTexture(), rhs.GetTexture());
|
||||
}
|
||||
|
||||
bool CKMaterialWrapperEqualTo::operator()(const CKMaterialWrapper& lhs, const CKMaterialWrapper& rhs) const {
|
||||
if (lhs.GetHash() != rhs.GetHash()) return false;
|
||||
return equal_to(lhs.GetMaterial(), rhs.GetMaterial());
|
||||
}
|
||||
|
||||
bool CKMeshWrapperEqualTo::operator()(const CKMeshWrapper& lhs, const CKMeshWrapper& rhs) const {
|
||||
if (lhs.GetHash() != rhs.GetHash()) return false;
|
||||
return equal_to(lhs.GetMesh(), rhs.GetMesh());
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Ruleset::Shared::DupCmp
|
||||
185
Ballance/BMapInspector/Ruleset/Shared/DupCmp.hpp
Normal file
185
Ballance/BMapInspector/Ruleset/Shared/DupCmp.hpp
Normal file
@@ -0,0 +1,185 @@
|
||||
#pragma once
|
||||
#include <VTAll.hpp>
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/macro/class_copy_move.hpp>
|
||||
#include <yycc/macro/ptr_size_detector.hpp>
|
||||
#include <utility>
|
||||
#include <optional>
|
||||
|
||||
#define BMAPINSP_L LibCmo
|
||||
#define BMAPINSP_C LibCmo::CK2
|
||||
#define BMAPINSP_O LibCmo::CK2::ObjImpls
|
||||
|
||||
namespace BMapInspector::Ruleset::Shared::DupCmp {
|
||||
|
||||
#pragma region Hash Combiner
|
||||
|
||||
/**
|
||||
* @brief FNV-1a Hash Combiner
|
||||
*/
|
||||
class Hasher {
|
||||
public:
|
||||
using ValueType = size_t;
|
||||
|
||||
private:
|
||||
#if defined(YYCC_PTRSIZE_32)
|
||||
static constexpr ValueType FNV_OFFSET_BASIS = 2166136261U;
|
||||
static constexpr ValueType FNV_PRIME = 16777619U;
|
||||
#else
|
||||
static constexpr ValueType FNV_OFFSET_BASIS = 14695981039346656037ULL;
|
||||
static constexpr ValueType FNV_PRIME = 1099511628211ULL;
|
||||
#endif
|
||||
|
||||
public:
|
||||
Hasher();
|
||||
~Hasher();
|
||||
YYCC_DEFAULT_COPY_MOVE(Hasher)
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Update this hash combiner with new hash.
|
||||
* @param h
|
||||
*/
|
||||
void combine(ValueType h);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Get final produced hash.
|
||||
* @return
|
||||
*/
|
||||
[[nodiscard]] ValueType finish() const noexcept { return this->seed; }
|
||||
template<typename T>
|
||||
void update(const T& v) {
|
||||
std::hash<T> hasher;
|
||||
combine(hasher(v));
|
||||
}
|
||||
template<typename T>
|
||||
void update_array(const T* addr, size_t cnt) {
|
||||
std::hash<T> hasher;
|
||||
for (size_t i = 0; i < cnt; ++i) {
|
||||
combine(hasher(addr[i]));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ValueType seed;
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Ruleset::Shared::DupCmp
|
||||
|
||||
namespace BMapInspector::Ruleset::Shared::DupCmp {
|
||||
|
||||
#pragma region CKObject Hash and Equal
|
||||
|
||||
struct CKTextureHash {
|
||||
[[nodiscard]] size_t operator()(const BMAPINSP_O::CKTexture* tex) const noexcept;
|
||||
};
|
||||
|
||||
struct CKMaterialHash {
|
||||
[[nodiscard]] size_t operator()(const BMAPINSP_O::CKMaterial* mtl) const noexcept;
|
||||
};
|
||||
|
||||
struct CKMeshHash {
|
||||
[[nodiscard]] size_t operator()(const BMAPINSP_O::CKMesh* _mesh) const noexcept;
|
||||
};
|
||||
|
||||
struct CKTextureEqualTo {
|
||||
[[nodiscard]] bool operator()(const BMAPINSP_O::CKTexture* lhs, const BMAPINSP_O::CKTexture* rhs) const;
|
||||
};
|
||||
|
||||
struct CKMaterialEqualTo {
|
||||
[[nodiscard]] bool operator()(const BMAPINSP_O::CKMaterial* lhs, const BMAPINSP_O::CKMaterial* rhs) const;
|
||||
};
|
||||
|
||||
struct CKMeshEqualTo {
|
||||
[[nodiscard]] bool operator()(const BMAPINSP_O::CKMesh* _lhs, const BMAPINSP_O::CKMesh* _rhs) const;
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region CKObject Wrapper
|
||||
|
||||
class CKTextureWrapper {
|
||||
public:
|
||||
CKTextureWrapper(BMAPINSP_O::CKTexture* texture);
|
||||
~CKTextureWrapper();
|
||||
YYCC_DEFAULT_COPY_MOVE(CKTextureWrapper)
|
||||
|
||||
public:
|
||||
BMAPINSP_O::CKTexture* GetTexture() const;
|
||||
size_t GetHash() const;
|
||||
|
||||
private:
|
||||
BMAPINSP_O::CKTexture* texture;
|
||||
CKTextureHash hasher;
|
||||
mutable std::optional<size_t> hash;
|
||||
};
|
||||
|
||||
class CKMaterialWrapper {
|
||||
public:
|
||||
CKMaterialWrapper(BMAPINSP_O::CKMaterial* material);
|
||||
~CKMaterialWrapper();
|
||||
YYCC_DEFAULT_COPY_MOVE(CKMaterialWrapper)
|
||||
|
||||
public:
|
||||
BMAPINSP_O::CKMaterial* GetMaterial() const;
|
||||
size_t GetHash() const;
|
||||
|
||||
private:
|
||||
BMAPINSP_O::CKMaterial* material;
|
||||
CKMaterialHash hasher;
|
||||
mutable std::optional<size_t> hash;
|
||||
};
|
||||
|
||||
class CKMeshWrapper {
|
||||
public:
|
||||
CKMeshWrapper(BMAPINSP_O::CKMesh* mesh);
|
||||
~CKMeshWrapper();
|
||||
YYCC_DEFAULT_COPY_MOVE(CKMeshWrapper)
|
||||
|
||||
public:
|
||||
BMAPINSP_O::CKMesh* GetMesh() const;
|
||||
size_t GetHash() const;
|
||||
|
||||
private:
|
||||
BMAPINSP_O::CKMesh* mesh;
|
||||
CKMeshHash hasher;
|
||||
mutable std::optional<size_t> hash;
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region CKObject Wrapper Hash and Equal
|
||||
|
||||
struct CKTextureWrapperHash {
|
||||
[[nodiscard]] size_t operator()(const CKTextureWrapper& tex) const noexcept;
|
||||
};
|
||||
|
||||
struct CKMaterialWrapperHash {
|
||||
[[nodiscard]] size_t operator()(const CKMaterialWrapper& mtl) const noexcept;
|
||||
};
|
||||
|
||||
struct CKMeshWrapperHash {
|
||||
[[nodiscard]] size_t operator()(const CKMeshWrapper& mesh) const noexcept;
|
||||
};
|
||||
|
||||
struct CKTextureWrapperEqualTo {
|
||||
CKTextureEqualTo equal_to;
|
||||
[[nodiscard]] bool operator()(const CKTextureWrapper& lhs, const CKTextureWrapper& rhs) const;
|
||||
};
|
||||
|
||||
struct CKMaterialWrapperEqualTo {
|
||||
CKMaterialEqualTo equal_to;
|
||||
[[nodiscard]] bool operator()(const CKMaterialWrapper& lhs, const CKMaterialWrapper& rhs) const;
|
||||
};
|
||||
|
||||
struct CKMeshWrapperEqualTo {
|
||||
CKMeshEqualTo equal_to;
|
||||
[[nodiscard]] bool operator()(const CKMeshWrapper& lhs, const CKMeshWrapper& rhs) const;
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Ruleset::Shared::DupCmp
|
||||
3
Ballance/BMapInspector/Ruleset/Shared/Name.cpp
Normal file
3
Ballance/BMapInspector/Ruleset/Shared/Name.cpp
Normal file
@@ -0,0 +1,3 @@
|
||||
#include "Name.hpp"
|
||||
|
||||
namespace BMapInspector::Ruleset::Shared::Name {}
|
||||
227
Ballance/BMapInspector/Ruleset/Shared/Name.hpp
Normal file
227
Ballance/BMapInspector/Ruleset/Shared/Name.hpp
Normal file
@@ -0,0 +1,227 @@
|
||||
#pragma once
|
||||
#include <array>
|
||||
|
||||
namespace BMapInspector::Ruleset::Shared::Name {
|
||||
|
||||
namespace Group {
|
||||
// clang-format off
|
||||
constexpr char8_t PS_LEVELSTART[] = u8"PS_Levelstart";
|
||||
constexpr char8_t PE_LEVELENDE[] = u8"PE_Levelende";
|
||||
constexpr char8_t PC_CHECKPOINTS[] = u8"PC_Checkpoints";
|
||||
constexpr char8_t PR_RESETPOINTS[] = u8"PR_Resetpoints";
|
||||
|
||||
constexpr char8_t PHYS_FLOORS[] = u8"Phys_Floors";
|
||||
constexpr char8_t PHYS_FLOORRAILS[] = u8"Phys_FloorRails";
|
||||
constexpr char8_t PHYS_FLOORSTOPPER[] = u8"Phys_FloorStopper";
|
||||
|
||||
constexpr std::array ALL_PH{
|
||||
u8"P_Extra_Life",
|
||||
u8"P_Extra_Point",
|
||||
u8"P_Trafo_Paper",
|
||||
u8"P_Trafo_Stone",
|
||||
u8"P_Trafo_Wood",
|
||||
u8"P_Ball_Paper",
|
||||
u8"P_Ball_Stone",
|
||||
u8"P_Ball_Wood",
|
||||
u8"P_Box",
|
||||
u8"P_Dome",
|
||||
u8"P_Modul_01",
|
||||
u8"P_Modul_03",
|
||||
u8"P_Modul_08",
|
||||
u8"P_Modul_17",
|
||||
u8"P_Modul_18",
|
||||
u8"P_Modul_19",
|
||||
u8"P_Modul_25",
|
||||
u8"P_Modul_26",
|
||||
u8"P_Modul_29",
|
||||
u8"P_Modul_30",
|
||||
u8"P_Modul_34",
|
||||
u8"P_Modul_37",
|
||||
u8"P_Modul_41",
|
||||
u8"PS_Levelstart",
|
||||
u8"PE_Levelende",
|
||||
u8"PC_Checkpoints",
|
||||
u8"PR_Resetpoints",
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
} // namespace Group
|
||||
|
||||
namespace Texture {
|
||||
// clang-format off
|
||||
constexpr char8_t RAIL_ENVIRONMENT[] = u8"Rail_Environment.bmp";
|
||||
constexpr char8_t LATERNE_VERLAUF[] = u8"Laterne_Verlauf.tga";
|
||||
|
||||
constexpr std::array OPAQUE_TEXS{
|
||||
u8"atari.bmp",
|
||||
u8"Ball_LightningSphere1.bmp",
|
||||
u8"Ball_LightningSphere2.bmp",
|
||||
u8"Ball_LightningSphere3.bmp",
|
||||
u8"Ball_Paper.bmp",
|
||||
u8"Ball_Stone.bmp",
|
||||
u8"Ball_Wood.bmp",
|
||||
u8"Brick.bmp",
|
||||
u8"Column_beige.bmp",
|
||||
u8"Column_blue.bmp",
|
||||
u8"Dome.bmp",
|
||||
u8"DomeEnvironment.bmp",
|
||||
u8"ExtraBall.bmp",
|
||||
u8"ExtraParticle.bmp",
|
||||
u8"E_Holzbeschlag.bmp",
|
||||
u8"FloorGlow.bmp",
|
||||
u8"Floor_Side.bmp",
|
||||
u8"Floor_Top_Border.bmp",
|
||||
u8"Floor_Top_Borderless.bmp",
|
||||
u8"Floor_Top_Checkpoint.bmp",
|
||||
u8"Floor_Top_Flat.bmp",
|
||||
u8"Floor_Top_Profil.bmp",
|
||||
u8"Floor_Top_ProfilFlat.bmp",
|
||||
u8"Gravitylogo_intro.bmp",
|
||||
u8"HardShadow.bmp",
|
||||
u8"Laterne_Glas.bmp",
|
||||
u8"Logo.bmp",
|
||||
u8"Metal_stained.bmp",
|
||||
u8"Misc_Ufo.bmp",
|
||||
u8"Misc_UFO_Flash.bmp",
|
||||
u8"Modul03_Floor.bmp",
|
||||
u8"Modul03_Wall.bmp",
|
||||
u8"Modul11_13_Wood.bmp",
|
||||
u8"Modul11_Wood.bmp",
|
||||
u8"Modul15.bmp",
|
||||
u8"Modul16.bmp",
|
||||
u8"Modul18.bmp",
|
||||
u8"Modul30_d_Seiten.bmp",
|
||||
u8"Particle_Flames.bmp",
|
||||
u8"Particle_Smoke.bmp",
|
||||
u8"PE_Bal_balloons.bmp",
|
||||
u8"PE_Bal_platform.bmp",
|
||||
u8"PE_Ufo_env.bmp",
|
||||
u8"P_Extra_Life_Oil.bmp",
|
||||
u8"P_Extra_Life_Particle.bmp",
|
||||
u8"P_Extra_Life_Shadow.bmp",
|
||||
u8"Rail_Environment.bmp",
|
||||
u8"sandsack.bmp",
|
||||
u8"SkyLayer.bmp",
|
||||
u8"Sky_Vortex.bmp",
|
||||
u8"Stick_Stripes.bmp",
|
||||
u8"Target.bmp",
|
||||
u8"Tower_Roof.bmp",
|
||||
u8"Trafo_Environment.bmp",
|
||||
u8"Trafo_FlashField.bmp",
|
||||
u8"Wood_Metal.bmp",
|
||||
u8"Wood_MetalStripes.bmp",
|
||||
u8"Wood_Misc.bmp",
|
||||
u8"Wood_Nailed.bmp",
|
||||
u8"Wood_Old.bmp",
|
||||
u8"Wood_Panel.bmp",
|
||||
u8"Wood_Plain.bmp",
|
||||
u8"Wood_Plain2.bmp",
|
||||
u8"Wood_Raft.bmp",
|
||||
};
|
||||
constexpr std::array TRANSPARENT_TEXS{
|
||||
u8"Button01_deselect.tga",
|
||||
u8"Button01_select.tga",
|
||||
u8"Button01_special.tga",
|
||||
u8"Column_beige_fade.tga",
|
||||
u8"Cursor.tga",
|
||||
u8"DomeShadow.tga",
|
||||
u8"Font_1.tga",
|
||||
u8"Laterne_Schatten.tga",
|
||||
u8"Laterne_Verlauf.tga",
|
||||
u8"Modul18_Gitter.tga",
|
||||
u8"Pfeil.tga",
|
||||
u8"Stick_Bottom.tga",
|
||||
u8"Trafo_Shadow_Big.tga",
|
||||
u8"Tut_Pfeil01.tga",
|
||||
u8"Tut_Pfeil_Hoch.tga",
|
||||
u8"Wolken_intro.tga",
|
||||
};
|
||||
|
||||
constexpr std::array ALL{
|
||||
// u8"atari.avi",
|
||||
u8"atari.bmp",
|
||||
u8"Ball_LightningSphere1.bmp",
|
||||
u8"Ball_LightningSphere2.bmp",
|
||||
u8"Ball_LightningSphere3.bmp",
|
||||
u8"Ball_Paper.bmp",
|
||||
u8"Ball_Stone.bmp",
|
||||
u8"Ball_Wood.bmp",
|
||||
u8"Brick.bmp",
|
||||
u8"Button01_deselect.tga",
|
||||
u8"Button01_select.tga",
|
||||
u8"Button01_special.tga",
|
||||
u8"Column_beige.bmp",
|
||||
u8"Column_beige_fade.tga",
|
||||
u8"Column_blue.bmp",
|
||||
u8"Cursor.tga",
|
||||
u8"Dome.bmp",
|
||||
u8"DomeEnvironment.bmp",
|
||||
u8"DomeShadow.tga",
|
||||
u8"ExtraBall.bmp",
|
||||
u8"ExtraParticle.bmp",
|
||||
u8"E_Holzbeschlag.bmp",
|
||||
u8"FloorGlow.bmp",
|
||||
u8"Floor_Side.bmp",
|
||||
u8"Floor_Top_Border.bmp",
|
||||
u8"Floor_Top_Borderless.bmp",
|
||||
u8"Floor_Top_Checkpoint.bmp",
|
||||
u8"Floor_Top_Flat.bmp",
|
||||
u8"Floor_Top_Profil.bmp",
|
||||
u8"Floor_Top_ProfilFlat.bmp",
|
||||
u8"Font_1.tga",
|
||||
u8"Gravitylogo_intro.bmp",
|
||||
u8"HardShadow.bmp",
|
||||
u8"Laterne_Glas.bmp",
|
||||
u8"Laterne_Schatten.tga",
|
||||
u8"Laterne_Verlauf.tga",
|
||||
u8"Logo.bmp",
|
||||
u8"Metal_stained.bmp",
|
||||
u8"Misc_Ufo.bmp",
|
||||
u8"Misc_UFO_Flash.bmp",
|
||||
u8"Modul03_Floor.bmp",
|
||||
u8"Modul03_Wall.bmp",
|
||||
u8"Modul11_13_Wood.bmp",
|
||||
u8"Modul11_Wood.bmp",
|
||||
u8"Modul15.bmp",
|
||||
u8"Modul16.bmp",
|
||||
u8"Modul18.bmp",
|
||||
u8"Modul18_Gitter.tga",
|
||||
u8"Modul30_d_Seiten.bmp",
|
||||
u8"Particle_Flames.bmp",
|
||||
u8"Particle_Smoke.bmp",
|
||||
u8"PE_Bal_balloons.bmp",
|
||||
u8"PE_Bal_platform.bmp",
|
||||
u8"PE_Ufo_env.bmp",
|
||||
u8"Pfeil.tga",
|
||||
u8"P_Extra_Life_Oil.bmp",
|
||||
u8"P_Extra_Life_Particle.bmp",
|
||||
u8"P_Extra_Life_Shadow.bmp",
|
||||
u8"Rail_Environment.bmp",
|
||||
u8"sandsack.bmp",
|
||||
u8"SkyLayer.bmp",
|
||||
u8"Sky_Vortex.bmp",
|
||||
u8"Stick_Bottom.tga",
|
||||
u8"Stick_Stripes.bmp",
|
||||
u8"Target.bmp",
|
||||
u8"Tower_Roof.bmp",
|
||||
u8"Trafo_Environment.bmp",
|
||||
u8"Trafo_FlashField.bmp",
|
||||
u8"Trafo_Shadow_Big.tga",
|
||||
u8"Tut_Pfeil01.tga",
|
||||
u8"Tut_Pfeil_Hoch.tga",
|
||||
u8"Wolken_intro.tga",
|
||||
u8"Wood_Metal.bmp",
|
||||
u8"Wood_MetalStripes.bmp",
|
||||
u8"Wood_Misc.bmp",
|
||||
u8"Wood_Nailed.bmp",
|
||||
u8"Wood_Old.bmp",
|
||||
u8"Wood_Panel.bmp",
|
||||
u8"Wood_Plain.bmp",
|
||||
u8"Wood_Plain2.bmp",
|
||||
u8"Wood_Raft.bmp",
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
} // namespace Texture
|
||||
|
||||
} // namespace BMapInspector::Ruleset::Shared::Name
|
||||
31
Ballance/BMapInspector/Ruleset/Shared/Sector.cpp
Normal file
31
Ballance/BMapInspector/Ruleset/Shared/Sector.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "Sector.hpp"
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/string/op.hpp>
|
||||
#include <VTAll.hpp>
|
||||
|
||||
namespace strop = yycc::string::op;
|
||||
namespace L = LibCmo;
|
||||
|
||||
namespace BMapInspector::Ruleset::Shared::Sector {
|
||||
|
||||
SectorNameBuilder::SectorNameBuilder() {}
|
||||
|
||||
SectorNameBuilder::~SectorNameBuilder() {}
|
||||
|
||||
SectorName SectorNameBuilder::get_name(L::CKDWORD sector) const {
|
||||
if (sector < MIN_SECTOR || sector > MAX_SECTOR) {
|
||||
throw std::logic_error("invalid sector number");
|
||||
} else {
|
||||
if (sector < 9) {
|
||||
return strop::printf(u8"Sector_%02" PRIuCKDWORD, sector);
|
||||
} else {
|
||||
return strop::printf(u8"Sector_%" PRIuCKDWORD, sector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Sector9Names SectorNameBuilder::get_sector9_names() const {
|
||||
return Sector9Names{.legacy_name = u8"Sector_9", .intuitive_name = u8"Sector_09"};
|
||||
}
|
||||
|
||||
}
|
||||
49
Ballance/BMapInspector/Ruleset/Shared/Sector.hpp
Normal file
49
Ballance/BMapInspector/Ruleset/Shared/Sector.hpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include <VTAll.hpp>
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/macro/class_copy_move.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace BMapInspector::Ruleset::Shared::Sector {
|
||||
|
||||
constexpr LibCmo::CKDWORD MIN_SECTOR = 1;
|
||||
constexpr LibCmo::CKDWORD MAX_SECTOR = 999;
|
||||
|
||||
/**
|
||||
* @brief The type for sector name.
|
||||
*/
|
||||
using SectorName = std::u8string;
|
||||
|
||||
struct Sector9Names {
|
||||
/** The Sector 9 name with "Sector_9" pattern which is accepted by all 999 sector loader */
|
||||
std::u8string legacy_name;
|
||||
/** The Sector 9 name with "Sector_09" pattern which is only accepted by new 999 sector loader */
|
||||
std::u8string intuitive_name;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The class for building Ballance sector group name.
|
||||
*/
|
||||
class SectorNameBuilder {
|
||||
public:
|
||||
SectorNameBuilder();
|
||||
~SectorNameBuilder();
|
||||
YYCC_DEFAULT_COPY_MOVE(SectorNameBuilder)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Get the sector name.
|
||||
* @param[in] sector The sector index.
|
||||
* @return Sector name.
|
||||
* @remarks
|
||||
* If you deliver sector index with 9, its return name is "Sector_9" which is accepted by all 999 sector loader.
|
||||
*/
|
||||
SectorName get_name(LibCmo::CKDWORD sector) const;
|
||||
/**
|
||||
* @brief Get the special sector 9 names.
|
||||
* @return Special built sector 9 names.
|
||||
*/
|
||||
Sector9Names get_sector9_names() const;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,14 +1,21 @@
|
||||
#include "Shared.hpp"
|
||||
#include "Utility.hpp"
|
||||
#include "Name.hpp"
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/carton/termcolor.hpp>
|
||||
#include <filesystem>
|
||||
#include <stdexcept>
|
||||
#include <cmath>
|
||||
|
||||
namespace strop = yycc::string::op;
|
||||
namespace termcolor = yycc::carton::termcolor;
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
|
||||
namespace BMapInspector::Rule::Shared {
|
||||
namespace BMapInspector::Ruleset::Shared::Utility {
|
||||
|
||||
#pragma region Utilities
|
||||
|
||||
#pragma region Check Functions
|
||||
|
||||
bool FPEqual(L::CKFLOAT lhs, L::CKFLOAT rhs, L::CKFLOAT tolerance) {
|
||||
auto diff = lhs - rhs;
|
||||
@@ -16,6 +23,10 @@ namespace BMapInspector::Rule::Shared {
|
||||
return absolute_diff <= tolerance;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Virtools Stuff
|
||||
|
||||
O::CKGroup* FetchGroup(C::CKContext* ctx, L::CKSTRING name) {
|
||||
return static_cast<O::CKGroup*>(ctx->GetObjectByNameAndClass(name, C::CK_CLASSID::CKCID_GROUP, nullptr));
|
||||
}
|
||||
@@ -29,28 +40,39 @@ namespace BMapInspector::Rule::Shared {
|
||||
}
|
||||
}
|
||||
|
||||
O::CKMaterial* FetchMaterial(C::CKContext* ctx, L::CKSTRING name) {
|
||||
return static_cast<O::CKMaterial*>(ctx->GetObjectByNameAndClass(name, C::CK_CLASSID::CKCID_MATERIAL, nullptr));
|
||||
}
|
||||
|
||||
std::vector<O::CK3dObject*> FetchPhysicalized3dObjects(C::CKContext* ctx) {
|
||||
std::vector<O::CK3dObject*> rv;
|
||||
|
||||
auto* phys_floors = FetchGroup(ctx, GroupNames::PHYS_FLOORS);
|
||||
auto* phys_floors = FetchGroup(ctx, Name::Group::PHYS_FLOORS);
|
||||
if (phys_floors != nullptr) Iter3dObjectsEx(rv, phys_floors);
|
||||
auto* phys_floorrails = FetchGroup(ctx, GroupNames::PHYS_FLOORRAILS);
|
||||
auto* phys_floorrails = FetchGroup(ctx, Name::Group::PHYS_FLOORRAILS);
|
||||
if (phys_floorrails != nullptr) Iter3dObjectsEx(rv, phys_floorrails);
|
||||
auto* phys_floorstopper = FetchGroup(ctx, GroupNames::PHYS_FLOORSTOPPER);
|
||||
auto* phys_floorstopper = FetchGroup(ctx, Name::Group::PHYS_FLOORSTOPPER);
|
||||
if (phys_floorstopper != nullptr) Iter3dObjectsEx(rv, phys_floorstopper);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool CheckTextureFileName(O::CKTexture* tex, L::CKSTRING name) {
|
||||
std::optional<std::u8string> ExtractTextureFileName(O::CKTexture* tex) {
|
||||
// Get file name
|
||||
auto filename = tex->GetUnderlyingData().GetSlotFileName(0);
|
||||
if (filename == nullptr) return false;
|
||||
if (filename == nullptr) return std::nullopt;
|
||||
// Extract file name part
|
||||
std::filesystem::path filepath(filename);
|
||||
auto filename_part = filepath.filename().u8string();
|
||||
return filename_part;
|
||||
}
|
||||
|
||||
bool CheckTextureFileName(O::CKTexture* tex, L::CKSTRING name) {
|
||||
// Get file name part
|
||||
auto filename_part = ExtractTextureFileName(tex);
|
||||
if (!filename_part.has_value()) return false;
|
||||
// Return result.
|
||||
return C::CKStrEqualI(filename_part.c_str(), name);
|
||||
return C::CKStrEqualI(filename_part.value().c_str(), name);
|
||||
}
|
||||
|
||||
std::vector<O::CK3dObject*> Iter3dObjects(O::CKGroup* group) {
|
||||
@@ -70,16 +92,31 @@ namespace BMapInspector::Rule::Shared {
|
||||
return rv;
|
||||
}
|
||||
|
||||
const char8_t* RenderObjectName(O::CKObject* obj) {
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Presentation
|
||||
|
||||
std::u8string QuoteText(const std::u8string_view& words) {
|
||||
std::u8string rv;
|
||||
rv.reserve(words.size() + 2);
|
||||
|
||||
rv.push_back('"');
|
||||
rv.append(words);
|
||||
rv.push_back('"');
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
std::u8string QuoteObjectName(O::CKObject* obj) {
|
||||
static std::u8string ANONYMOUS = termcolor::colored(u8"<anonymous>", termcolor::Color::LightMagenta);
|
||||
auto name = obj->GetName();
|
||||
if (name == nullptr) {
|
||||
return ANONYMOUS.c_str();
|
||||
return QuoteText(ANONYMOUS);
|
||||
} else {
|
||||
return name;
|
||||
return QuoteText(name);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Rule::Shared
|
||||
} // namespace BMapInspector::Ruleset::Shared::Utility
|
||||
118
Ballance/BMapInspector/Ruleset/Shared/Utility.hpp
Normal file
118
Ballance/BMapInspector/Ruleset/Shared/Utility.hpp
Normal file
@@ -0,0 +1,118 @@
|
||||
#pragma once
|
||||
#include <VTAll.hpp>
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/string/op.hpp>
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
|
||||
#define BMAPINSP_L LibCmo
|
||||
#define BMAPINSP_C LibCmo::CK2
|
||||
#define BMAPINSP_O LibCmo::CK2::ObjImpls
|
||||
|
||||
namespace BMapInspector::Ruleset::Shared::Utility {
|
||||
|
||||
#pragma region Utilities
|
||||
|
||||
/**
|
||||
* @brief Check whether given 2 float point values are equal with given tolerance.
|
||||
* @param[in] lhs The left value to compare.
|
||||
* @param[in] rhs The right value to compare.
|
||||
* @param[in] tolerance The tolerance to compare.
|
||||
* @return True if they are equal with given tolerance, otherwise false.
|
||||
*/
|
||||
bool FPEqual(BMAPINSP_L::CKFLOAT lhs, BMAPINSP_L::CKFLOAT rhs, BMAPINSP_L::CKFLOAT tolerance);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Virtools Stuff
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* @param[in] ctx Can not be nullptr.
|
||||
* @param[in] name Can not be nullptr.
|
||||
* @return Found pointer to CKGroup, otherwise nullptr.
|
||||
*/
|
||||
BMAPINSP_O::CKGroup* FetchGroup(BMAPINSP_C::CKContext* ctx, BMAPINSP_L::CKSTRING name);
|
||||
/**
|
||||
* @brief
|
||||
* @param[in] ctx Can not be nullptr.
|
||||
* @param[in] name Can not be nullptr.
|
||||
* @return Found pointer to CKMaterial, otherwise nullptr.
|
||||
*/
|
||||
BMAPINSP_O::CKMaterial* FetchMaterial(BMAPINSP_C::CKContext* ctx, BMAPINSP_L::CKSTRING name);
|
||||
std::vector<BMAPINSP_O::CK3dObject*> FetchPhysicalized3dObjects(BMAPINSP_C::CKContext* ctx);
|
||||
|
||||
/**
|
||||
* @brief Extract the file name part of the texture slot associated file path in given CKTexture.
|
||||
* @param[in] tex The texture for extracting. Can not be nullptr.
|
||||
* @return Extracted file name part or nothing (there is no associated file path).
|
||||
*/
|
||||
std::optional<std::u8string> ExtractTextureFileName(BMAPINSP_O::CKTexture* tex);
|
||||
/**
|
||||
* @brief Check whether given CKTexture has the given file name (case-insensitive).
|
||||
* @param[in] tex Can not be nullptr.
|
||||
* @param[in] name Can not be nullptr.
|
||||
* @return True if it is, otherwise false.
|
||||
*/
|
||||
bool CheckTextureFileName(BMAPINSP_O::CKTexture* tex, BMAPINSP_L::CKSTRING name);
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* @param[in] group Can not be nullptr.
|
||||
* @return All objects is the child class of CK3dEntity.
|
||||
*/
|
||||
std::vector<BMAPINSP_O::CK3dObject*> Iter3dObjects(BMAPINSP_O::CKGroup* group);
|
||||
/**
|
||||
* @brief
|
||||
* @param[in] mesh Can not be nullptr.
|
||||
* @return All nullptr reference are removed.
|
||||
*/
|
||||
std::vector<BMAPINSP_O::CKMaterial*> IterMaterial(BMAPINSP_O::CKMesh* mesh);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Presentation
|
||||
|
||||
/**
|
||||
* @brief Quote given string.
|
||||
* @param[in] words The words to quote.
|
||||
* @return The quoted words.
|
||||
*/
|
||||
std::u8string QuoteText(const std::u8string_view& words);
|
||||
/**
|
||||
* @brief Quote given CKObject's name.
|
||||
* @param[in] obj The CKObject for quoting. Can not be nullptr.
|
||||
* @remarks If there is no name for given CKObject, a colorful name will be quoted and return.
|
||||
* @return The quoted name.
|
||||
*/
|
||||
std::u8string QuoteObjectName(BMAPINSP_O::CKObject* obj);
|
||||
/**
|
||||
* @brief Quote given range of CKObject's names.
|
||||
* @tparam InputIt The type of iterator which iterate non-nullptr pointer to CKObject.
|
||||
* @param[in] first The iterator to the first element.
|
||||
* @param[in] last The iterator to the last element.
|
||||
* @return The quoted names with quote as separator.
|
||||
*/
|
||||
template<std::input_iterator InputIt>
|
||||
requires std::is_pointer_v<std::iter_value_t<InputIt>>
|
||||
&& std::is_base_of_v<BMAPINSP_O::CKObject, std::remove_pointer_t<std::iter_value_t<InputIt>>>
|
||||
std::u8string QuoteObjectNames(InputIt first, InputIt last) {
|
||||
std::u8string cache;
|
||||
return yycc::string::op::join(
|
||||
[&cache, &first, &last]() -> std::optional<std::u8string_view> {
|
||||
if (first == last) return std::nullopt;
|
||||
// YYC MARK:
|
||||
// We must use "cache", otherwise "use after free" will occur.
|
||||
cache = QuoteObjectName(*(first++));
|
||||
return cache;
|
||||
},
|
||||
u8", ");
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Ruleset::Shared::Utility
|
||||
|
||||
#undef BMAPINSP_O
|
||||
#undef BMAPINSP_C
|
||||
#undef BMAPINSP_L
|
||||
374
Ballance/BMapInspector/Ruleset/YYCRules.cpp
Normal file
374
Ballance/BMapInspector/Ruleset/YYCRules.cpp
Normal file
@@ -0,0 +1,374 @@
|
||||
#include "YYCRules.hpp"
|
||||
#include "Shared/Utility.hpp"
|
||||
#include "Shared/Name.hpp"
|
||||
#include "Shared/DupCmp.hpp"
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/string/op.hpp>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <optional>
|
||||
#include <algorithm>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace V = LibCmo::VxMath;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
namespace strop = yycc::string::op;
|
||||
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
#pragma region YYC Rule 1
|
||||
|
||||
YYCRule1::YYCRule1() : Rule::IRule() {}
|
||||
|
||||
YYCRule1::~YYCRule1() {}
|
||||
|
||||
std::u8string_view YYCRule1::GetRuleName() const {
|
||||
return u8"YYC1";
|
||||
}
|
||||
|
||||
void YYCRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
auto* ctx = level.GetCKContext();
|
||||
|
||||
// We get "Phys_FloorRails" group first.
|
||||
auto* phys_floorrails = Shared::Utility::FetchGroup(ctx, Shared::Name::Group::PHYS_FLOORRAILS);
|
||||
if (phys_floorrails == nullptr) return;
|
||||
|
||||
// Create container holding smooth meshes
|
||||
std::set<O::CKMesh*> smooth_meshes;
|
||||
// We iterate all object grouped into it.
|
||||
auto group_3dobjects = Shared::Utility::Iter3dObjects(phys_floorrails);
|
||||
for (auto* group_3dobject : group_3dobjects) {
|
||||
// Then we iterate their current meshes
|
||||
auto* mesh = group_3dobject->GetCurrentMesh();
|
||||
if (mesh == nullptr) continue;
|
||||
|
||||
// Iterate all meshes
|
||||
auto mtls = Shared::Utility::IterMaterial(mesh);
|
||||
for (auto* mtl : mtls) {
|
||||
// Check whether all texture referred by this mesh are "Rail_Environment".
|
||||
auto texture = mtl->GetTexture();
|
||||
if (texture == nullptr) continue;
|
||||
if (!Shared::Utility::CheckTextureFileName(texture, Shared::Name::Texture::RAIL_ENVIRONMENT)) {
|
||||
// No, this is not rail texture, throw error.
|
||||
reporter.FormatError(
|
||||
u8"Object %s is grouped into %s, but its texture %s (referred by mesh %s and material %s) seems not the rail texture. "
|
||||
u8"This will cause some parts of this object be smooth unexpectly.",
|
||||
Shared::Utility::QuoteObjectName(group_3dobject).c_str(),
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PHYS_FLOORRAILS).c_str(),
|
||||
Shared::Utility::QuoteObjectName(texture).c_str(),
|
||||
Shared::Utility::QuoteObjectName(mesh).c_str(),
|
||||
Shared::Utility::QuoteObjectName(mtl).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Record this mesh into set.
|
||||
smooth_meshes.emplace(mesh);
|
||||
}
|
||||
|
||||
// Now we make sure that these smooth mesh is not referred by any other object.
|
||||
// We iterate all 3d object first
|
||||
auto all_3dobject = level.Get3dObjects();
|
||||
for (auto* obj : all_3dobject) {
|
||||
// Then we get its current mesh
|
||||
auto* mesh = obj->GetCurrentMesh();
|
||||
if (mesh == nullptr) continue;
|
||||
|
||||
// Check whether its mesh is in smooth mesh,
|
||||
// and itself is not in "Phys_FloorRails" group
|
||||
if (!obj->IsInGroup(phys_floorrails) && smooth_meshes.contains(mesh)) {
|
||||
// Report error.
|
||||
reporter.FormatError(u8"Object %s is not grouped into %s, but some objects grouped into %s refer its mesh %s. "
|
||||
u8"This will cause this object be smooth unexpectly.",
|
||||
Shared::Utility::QuoteObjectName(obj).c_str(),
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PHYS_FLOORRAILS).c_str(),
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PHYS_FLOORRAILS).c_str(),
|
||||
Shared::Utility::QuoteObjectName(mesh).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region YYC Rule 2
|
||||
|
||||
YYCRule2::YYCRule2() : Rule::IRule() {}
|
||||
|
||||
YYCRule2::~YYCRule2() {}
|
||||
|
||||
std::u8string_view YYCRule2::GetRuleName() const {
|
||||
return u8"YYC2";
|
||||
}
|
||||
|
||||
void YYCRule2::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
auto* ctx = level.GetCKContext();
|
||||
auto physicalized_3dobjects = Shared::Utility::FetchPhysicalized3dObjects(ctx);
|
||||
|
||||
// Iterate all physicalized 3dobject
|
||||
for (auto* physicalized_3dobject : physicalized_3dobjects) {
|
||||
// Get its mesh
|
||||
auto* mesh = physicalized_3dobject->GetCurrentMesh();
|
||||
if (mesh == nullptr) continue;
|
||||
|
||||
// Create a bool vector with vertex count and false init value.
|
||||
std::vector<bool> used_vertex(mesh->GetVertexCount(), false);
|
||||
// Iterate all face and set their vertex as used.
|
||||
auto* face_indices = mesh->GetFaceIndices();
|
||||
for (L::CKDWORD face_idx = 0; face_idx < mesh->GetFaceCount(); ++face_idx) {
|
||||
used_vertex[face_indices[face_idx * 3]] = true;
|
||||
used_vertex[face_indices[face_idx * 3 + 1]] = true;
|
||||
used_vertex[face_indices[face_idx * 3 + 2]] = true;
|
||||
}
|
||||
// Check whether there is unused vertex
|
||||
auto has_unused_vertex = std::any_of(used_vertex.begin(), used_vertex.end(), [](bool v) { return v == false; });
|
||||
// If there is unused vertex, report error
|
||||
if (has_unused_vertex) {
|
||||
reporter.FormatError(u8"Object %s is grouped into physicalization groups, and its referred mesh %s has isolated vertex. "
|
||||
u8"This will cause it can not be physicalized.",
|
||||
Shared::Utility::QuoteObjectName(physicalized_3dobject).c_str(),
|
||||
Shared::Utility::QuoteObjectName(mesh).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region YYC Rule 3
|
||||
|
||||
YYCRule3::YYCRule3() : Rule::IRule() {}
|
||||
|
||||
YYCRule3::~YYCRule3() {}
|
||||
|
||||
std::u8string_view YYCRule3::GetRuleName() const {
|
||||
return u8"YYC3";
|
||||
}
|
||||
|
||||
void YYCRule3::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
// Using utility structs
|
||||
using Shared::DupCmp::CKMaterialWrapper;
|
||||
using Shared::DupCmp::CKMaterialWrapperEqualTo;
|
||||
using Shared::DupCmp::CKMaterialWrapperHash;
|
||||
using Shared::DupCmp::CKMeshWrapper;
|
||||
using Shared::DupCmp::CKMeshWrapperEqualTo;
|
||||
using Shared::DupCmp::CKMeshWrapperHash;
|
||||
using Shared::DupCmp::CKTextureWrapper;
|
||||
using Shared::DupCmp::CKTextureWrapperEqualTo;
|
||||
using Shared::DupCmp::CKTextureWrapperHash;
|
||||
|
||||
// Check textures
|
||||
std::unordered_multiset<CKTextureWrapper, CKTextureWrapperHash, CKTextureWrapperEqualTo> textures;
|
||||
for (auto* tex : level.GetTextures()) {
|
||||
textures.emplace(CKTextureWrapper(tex));
|
||||
}
|
||||
// Show result
|
||||
for (auto it = textures.begin(); it != textures.end();) {
|
||||
size_t count = textures.count(*it);
|
||||
|
||||
// all count elements have equivalent keys
|
||||
if (count > 1) {
|
||||
std::vector<O::CKTexture*> dup_texs;
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
dup_texs.emplace_back(it->GetTexture());
|
||||
++it;
|
||||
}
|
||||
|
||||
reporter.FormatInfo(u8"Some textures are visually identical. Please consider merging them to reduce the final map size. "
|
||||
u8"These textures are: %s.",
|
||||
Shared::Utility::QuoteObjectNames(dup_texs.begin(), dup_texs.end()).c_str());
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// Check materials
|
||||
std::unordered_multiset<CKMaterialWrapper, CKMaterialWrapperHash, CKMaterialWrapperEqualTo> materials;
|
||||
for (auto* mat : level.GetMaterials()) {
|
||||
materials.emplace(CKMaterialWrapper(mat));
|
||||
}
|
||||
// Show result
|
||||
for (auto it = materials.begin(); it != materials.end();) {
|
||||
size_t count = materials.count(*it);
|
||||
|
||||
// all count elements have equivalent keys
|
||||
if (count > 1) {
|
||||
std::vector<O::CKMaterial*> dup_mtls;
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
dup_mtls.emplace_back(it->GetMaterial());
|
||||
++it;
|
||||
}
|
||||
|
||||
reporter.FormatInfo(u8"Some materials are visually identical. Please consider merging them to reduce the final map size. "
|
||||
u8"These materials are: %s.",
|
||||
Shared::Utility::QuoteObjectNames(dup_mtls.begin(), dup_mtls.end()).c_str());
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// Check meshes
|
||||
std::unordered_multiset<CKMeshWrapper, CKMeshWrapperHash, CKMeshWrapperEqualTo> meshes;
|
||||
for (auto* mesh : level.GetMeshes()) {
|
||||
meshes.emplace(CKMeshWrapper(mesh));
|
||||
}
|
||||
// Show result
|
||||
for (auto it = meshes.begin(); it != meshes.end();) {
|
||||
size_t count = meshes.count(*it);
|
||||
|
||||
// all count elements have equivalent keys
|
||||
if (count > 1) {
|
||||
std::vector<O::CKMesh*> dup_meshes;
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
dup_meshes.emplace_back(it->GetMesh());
|
||||
++it;
|
||||
}
|
||||
|
||||
reporter.FormatInfo(u8"Some meshes are visually identical. Please consider merging them to reduce the final map size. "
|
||||
u8"These meshes are: %s.",
|
||||
Shared::Utility::QuoteObjectNames(dup_meshes.begin(), dup_meshes.end()).c_str());
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region YYC Rule 4
|
||||
|
||||
YYCRule4::YYCRule4() : Rule::IRule() {}
|
||||
|
||||
YYCRule4::~YYCRule4() {}
|
||||
|
||||
std::u8string_view YYCRule4::GetRuleName() const {
|
||||
return u8"YYC4";
|
||||
}
|
||||
|
||||
void YYCRule4::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
// Build lowercase texture name set first.
|
||||
std::set<std::u8string> opaque_texs;
|
||||
for (const auto* tex_name : Shared::Name::Texture::OPAQUE_TEXS) {
|
||||
opaque_texs.emplace(strop::to_lower(tex_name));
|
||||
}
|
||||
std::set<std::u8string> transparent_texs;
|
||||
for (const auto* tex_name : Shared::Name::Texture::TRANSPARENT_TEXS) {
|
||||
transparent_texs.emplace(strop::to_lower(tex_name));
|
||||
}
|
||||
|
||||
// Check texture one by one
|
||||
for (auto& tex : level.GetTextures()) {
|
||||
auto tex_filename = Shared::Utility::ExtractTextureFileName(tex);
|
||||
if (!tex_filename.has_value()) continue;
|
||||
|
||||
auto lower_tex_filename = strop::to_lower(tex_filename.value());
|
||||
if (opaque_texs.contains(lower_tex_filename)) {
|
||||
if (tex->GetVideoFormat() != V::VX_PIXELFORMAT::_16_ARGB1555) {
|
||||
reporter.FormatInfo(u8"Texture %s is Ballance opaque texture. But its video format is not ARGB1555. "
|
||||
u8"This is mismatched with vanilla Ballance.",
|
||||
Shared::Utility::QuoteObjectName(tex).c_str());
|
||||
}
|
||||
} else if (transparent_texs.contains(lower_tex_filename)) {
|
||||
if (tex->GetVideoFormat() != V::VX_PIXELFORMAT::_32_ARGB8888) {
|
||||
reporter.FormatInfo(u8"Texture %s is Ballance transparent texture. But its video format is not ARGB8888. "
|
||||
u8"This is mismatched with vanilla Ballance.",
|
||||
Shared::Utility::QuoteObjectName(tex).c_str());
|
||||
}
|
||||
} else {
|
||||
switch (tex->GetVideoFormat()) {
|
||||
case V::VX_PIXELFORMAT::_16_ARGB1555:
|
||||
// Do nothing.
|
||||
break;
|
||||
case V::VX_PIXELFORMAT::_32_ARGB8888:
|
||||
reporter.FormatInfo(u8"Texture %s is not Ballance texture. Its video format is ARGB8888. "
|
||||
u8"This may cause useless performance consumption if there is no transparent inside it. "
|
||||
u8"Please check whether this is essential.",
|
||||
Shared::Utility::QuoteObjectName(tex).c_str());
|
||||
break;
|
||||
default:
|
||||
reporter.FormatInfo(
|
||||
u8"Texture %s is not Ballance texture. Its video format is not ARGB1555 or ARGB8888. "
|
||||
u8"This is mismatched with vanilla Ballance. "
|
||||
u8"Please set it to ARGB1555 for opaque texture, or ARGB8888 for transaprent texture, except special scenario.",
|
||||
Shared::Utility::QuoteObjectName(tex).c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region YYC Rule 5
|
||||
|
||||
YYCRule5::YYCRule5() : Rule::IRule() {}
|
||||
|
||||
YYCRule5::~YYCRule5() {}
|
||||
|
||||
std::u8string_view YYCRule5::GetRuleName() const {
|
||||
return u8"YYC5";
|
||||
}
|
||||
|
||||
void YYCRule5::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
// Build lowercase texture name set first.
|
||||
std::set<std::u8string> texs;
|
||||
for (const auto* tex_name : Shared::Name::Texture::ALL) {
|
||||
texs.emplace(strop::to_lower(tex_name));
|
||||
}
|
||||
|
||||
// Check texture one by one
|
||||
for (auto& tex : level.GetTextures()) {
|
||||
auto tex_filename = Shared::Utility::ExtractTextureFileName(tex);
|
||||
if (!tex_filename.has_value()) continue;
|
||||
|
||||
auto lower_tex_filename = strop::to_lower(tex_filename.value());
|
||||
bool is_ballance_tex = texs.contains(lower_tex_filename);
|
||||
|
||||
using C::CK_TEXTURE_SAVEOPTIONS;
|
||||
switch (tex->GetUnderlyingData().GetSaveOptions()) {
|
||||
case CK_TEXTURE_SAVEOPTIONS::CKTEXTURE_USEGLOBAL:
|
||||
reporter.FormatInfo(u8"The save option of texture %s rely on global Virtools settings. "
|
||||
u8"This cause ambiguity and different behavior on different Virtools by different user settings. "
|
||||
u8"Please consider change it to explicit option, such as External or Raw Data.",
|
||||
Shared::Utility::QuoteObjectName(tex).c_str());
|
||||
break;
|
||||
case CK_TEXTURE_SAVEOPTIONS::CKTEXTURE_EXTERNAL:
|
||||
if (!is_ballance_tex) {
|
||||
reporter.FormatWarning(u8"Texture %s is not Ballance texture, but its save option is External. "
|
||||
u8"This may cause texture loss when rendering in game. Please consider store it inside map.",
|
||||
Shared::Utility::QuoteObjectName(tex).c_str());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (is_ballance_tex) {
|
||||
reporter.FormatInfo(u8"Texture %s is Ballance texture, but its save option is not External. "
|
||||
u8"Please consider storing it as External to reduce the final size of map file, "
|
||||
u8"and let user specified texture pack work.",
|
||||
Shared::Utility::QuoteObjectName(tex).c_str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region YYC Rule 6
|
||||
|
||||
YYCRule6::YYCRule6() : Rule::IRule() {}
|
||||
|
||||
YYCRule6::~YYCRule6() {}
|
||||
|
||||
std::u8string_view YYCRule6::GetRuleName() const {
|
||||
return u8"YYC6";
|
||||
}
|
||||
|
||||
void YYCRule6::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
// TODO:
|
||||
// This rule is not so essential.
|
||||
// So we are not urgently to implement it in there.
|
||||
// Just make a rule placeholder in there and may finish it in future.
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Ruleset
|
||||
106
Ballance/BMapInspector/Ruleset/YYCRules.hpp
Normal file
106
Ballance/BMapInspector/Ruleset/YYCRules.hpp
Normal file
@@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
#include "../Rule.hpp"
|
||||
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
/**
|
||||
* @brief YYC12345 Rule 1
|
||||
* @details
|
||||
* The object grouped into "Phys_FloorRails" should only be rails, otherwise their meshes' UV will be smooth.
|
||||
* Additionally, these smooth UV meshes will also affect those objects refering them.
|
||||
*/
|
||||
class YYCRule1 : public Rule::IRule {
|
||||
public:
|
||||
YYCRule1();
|
||||
virtual ~YYCRule1();
|
||||
YYCC_DELETE_COPY_MOVE(YYCRule1)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief YYC12345 Rule 2
|
||||
* @details
|
||||
* The object grouped into physicalization group should not have isolated vertex,
|
||||
* otherwise it will fail to be physicalized.
|
||||
*/
|
||||
class YYCRule2 : public Rule::IRule {
|
||||
public:
|
||||
YYCRule2();
|
||||
virtual ~YYCRule2();
|
||||
YYCC_DELETE_COPY_MOVE(YYCRule2)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief YYC12345 Rule 3
|
||||
* @details
|
||||
* Exactly same mesh, material and texture can be merged.
|
||||
*/
|
||||
class YYCRule3 : public Rule::IRule {
|
||||
public:
|
||||
YYCRule3();
|
||||
virtual ~YYCRule3();
|
||||
YYCC_DELETE_COPY_MOVE(YYCRule3)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief YYC12345 Rule 4
|
||||
* @details
|
||||
* Check the video format for Ballance and user-defined texture respectively.
|
||||
* Report if there is non-vanilla Ballance settings.
|
||||
*/
|
||||
class YYCRule4 : public Rule::IRule {
|
||||
public:
|
||||
YYCRule4();
|
||||
virtual ~YYCRule4();
|
||||
YYCC_DELETE_COPY_MOVE(YYCRule4)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief YYC12345 Rule 5
|
||||
* @details
|
||||
* Check the save options for Ballance and user-defined texture respectively
|
||||
* for reducing map size and avoid ambiguity.
|
||||
*/
|
||||
class YYCRule5 : public Rule::IRule {
|
||||
public:
|
||||
YYCRule5();
|
||||
virtual ~YYCRule5();
|
||||
YYCC_DELETE_COPY_MOVE(YYCRule5)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief YYC12345 Rule 6
|
||||
* @details
|
||||
* Show info hint for the skip of progressbar when loading map.
|
||||
*/
|
||||
class YYCRule6 : public Rule::IRule {
|
||||
public:
|
||||
YYCRule6();
|
||||
virtual ~YYCRule6();
|
||||
YYCC_DELETE_COPY_MOVE(YYCRule6)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
}
|
||||
224
Ballance/BMapInspector/Ruleset/ZZQRules.cpp
Normal file
224
Ballance/BMapInspector/Ruleset/ZZQRules.cpp
Normal file
@@ -0,0 +1,224 @@
|
||||
#include "ZZQRules.hpp"
|
||||
#include "Shared/Utility.hpp"
|
||||
#include "Shared/Name.hpp"
|
||||
#include "Shared/Sector.hpp"
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
#pragma region ZZQ Rule 1
|
||||
|
||||
ZZQRule1::ZZQRule1() : Rule::IRule() {}
|
||||
|
||||
ZZQRule1::~ZZQRule1() {}
|
||||
|
||||
std::u8string_view ZZQRule1::GetRuleName() const {
|
||||
return u8"ZZQ1";
|
||||
}
|
||||
|
||||
void ZZQRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
auto* ctx = level.GetCKContext();
|
||||
|
||||
// We get "Phys_FloorStopper" group first.
|
||||
auto* phys_floorstopper = Shared::Utility::FetchGroup(ctx, Shared::Name::Group::PHYS_FLOORSTOPPER);
|
||||
if (phys_floorstopper == nullptr) return;
|
||||
// We iterate all object grouped into it.
|
||||
auto group_3dobjects = Shared::Utility::Iter3dObjects(phys_floorstopper);
|
||||
|
||||
// Show the first object if it have.
|
||||
if (!group_3dobjects.empty()) {
|
||||
auto* first_3dobjects = group_3dobjects.front();
|
||||
reporter.FormatInfo(u8"Object %s is the first object grouped into %s. "
|
||||
u8"It is the only stopper which can make sound in game.",
|
||||
Shared::Utility::QuoteObjectName(first_3dobjects).c_str(),
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PHYS_FLOORSTOPPER).c_str());
|
||||
}
|
||||
|
||||
// Warning for other objects
|
||||
for (size_t i = 1; i < group_3dobjects.size(); ++i) {
|
||||
auto* other_3dobject = group_3dobjects[i];
|
||||
reporter.FormatWarning(u8"Object %s is grouped into %s but it is not the only object. "
|
||||
u8"This will cause it can not make sound in game. Please confirm this is by your intention. "
|
||||
u8"If you want it can make sound, please join it into the first object located in that group.",
|
||||
Shared::Utility::QuoteObjectName(other_3dobject).c_str(),
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PHYS_FLOORSTOPPER).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region ZZQ Rule 2
|
||||
|
||||
ZZQRule2::ZZQRule2() : Rule::IRule() {}
|
||||
|
||||
ZZQRule2::~ZZQRule2() {}
|
||||
|
||||
std::u8string_view ZZQRule2::GetRuleName() const {
|
||||
return u8"ZZQ2";
|
||||
}
|
||||
|
||||
void ZZQRule2::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
auto* ctx = level.GetCKContext();
|
||||
Shared::Sector::SectorNameBuilder builder;
|
||||
|
||||
// Extract group objects info
|
||||
std::vector<std::set<O::CK3dObject*>> sector_objects;
|
||||
for (L::CKDWORD i = Shared::Sector::MIN_SECTOR; i <= Shared::Sector::MAX_SECTOR; ++i) {
|
||||
// Prepare inserted object set.
|
||||
std::set<O::CK3dObject*> object_set;
|
||||
|
||||
// Build name first with special treat for sector 9
|
||||
// and fill objects into set.
|
||||
if (i != 9) {
|
||||
auto sector_name = builder.get_name(i);
|
||||
auto* sector = Shared::Utility::FetchGroup(ctx, sector_name.c_str());
|
||||
if (sector == nullptr) break;
|
||||
|
||||
auto group_3dobjects = Shared::Utility::Iter3dObjects(sector);
|
||||
for (auto* group_3dobject : group_3dobjects) {
|
||||
object_set.emplace(group_3dobject);
|
||||
}
|
||||
} else {
|
||||
auto sector_names = builder.get_sector9_names();
|
||||
auto* legacy_sector = Shared::Utility::FetchGroup(ctx, sector_names.legacy_name.c_str());
|
||||
auto* intuitive_sector = Shared::Utility::FetchGroup(ctx, sector_names.intuitive_name.c_str());
|
||||
if (legacy_sector == nullptr && intuitive_sector == nullptr) break;
|
||||
|
||||
if (legacy_sector != nullptr) {
|
||||
auto group_3dobjects = Shared::Utility::Iter3dObjects(legacy_sector);
|
||||
for (auto* group_3dobject : group_3dobjects) {
|
||||
object_set.emplace(group_3dobject);
|
||||
}
|
||||
}
|
||||
if (intuitive_sector != nullptr) {
|
||||
auto group_3dobjects = Shared::Utility::Iter3dObjects(intuitive_sector);
|
||||
for (auto* group_3dobject : group_3dobjects) {
|
||||
object_set.emplace(group_3dobject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Insert object set
|
||||
sector_objects.emplace_back(std::move(object_set));
|
||||
}
|
||||
|
||||
// Check the intersection one by one
|
||||
for (size_t i = 0; i < sector_objects.size(); ++i) {
|
||||
for (size_t j = i + 1; j < sector_objects.size(); ++j) {
|
||||
// Fetch 2 set repsectively.
|
||||
const auto& left_sector = sector_objects[i];
|
||||
const auto& right_sector = sector_objects[j];
|
||||
// Check duplicated objects
|
||||
std::vector<O::CK3dObject*> intersection;
|
||||
std::set_intersection(left_sector.begin(),
|
||||
left_sector.end(),
|
||||
right_sector.begin(),
|
||||
right_sector.end(),
|
||||
std::back_inserter(intersection));
|
||||
|
||||
// Output if there is intersection
|
||||
if (!intersection.empty()) {
|
||||
// Get sector index.
|
||||
auto left_sector_idx = static_cast<L::CKDWORD>(i + 1);
|
||||
auto right_sector_idx = static_cast<L::CKDWORD>(j + 1);
|
||||
|
||||
// Output result.
|
||||
reporter.FormatWarning(u8"Some objects are grouped into sector %" PRIuCKDWORD " and sector %" PRIuCKDWORD
|
||||
" represented group bothly. This is not allowed. These objects are: %s.",
|
||||
left_sector_idx,
|
||||
right_sector_idx,
|
||||
Shared::Utility::QuoteObjectNames(intersection.begin(), intersection.end()).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region ZZQ Rule 3
|
||||
|
||||
ZZQRule3::ZZQRule3() : Rule::IRule() {}
|
||||
|
||||
ZZQRule3::~ZZQRule3() {}
|
||||
|
||||
std::u8string_view ZZQRule3::GetRuleName() const {
|
||||
return u8"ZZQ3";
|
||||
}
|
||||
|
||||
void ZZQRule3::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
auto* ctx = level.GetCKContext();
|
||||
Shared::Sector::SectorNameBuilder builder;
|
||||
|
||||
auto* level_start = Shared::Utility::FetchGroup(ctx, Shared::Name::Group::PS_LEVELSTART);
|
||||
if (level_start == nullptr) {
|
||||
reporter.FormatError(u8"Incomplete level: can not find %s group.",
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PS_LEVELSTART).c_str());
|
||||
} else {
|
||||
switch (level_start->GetObjectCount()) {
|
||||
case 0:
|
||||
reporter.FormatError(u8"Incomplete level: there is no object grouped into %s group.",
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PS_LEVELSTART).c_str());
|
||||
break;
|
||||
case 1:
|
||||
// OK. Do nothing.
|
||||
break;
|
||||
default:
|
||||
reporter.FormatError(u8"Bad level: there are more than one objects grouped into %s group.",
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PS_LEVELSTART).c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto* level_end = Shared::Utility::FetchGroup(ctx, Shared::Name::Group::PE_LEVELENDE);
|
||||
if (level_end == nullptr) {
|
||||
reporter.FormatError(u8"Incomplete level: can not find %s group.",
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PE_LEVELENDE).c_str());
|
||||
} else {
|
||||
switch (level_end->GetObjectCount()) {
|
||||
case 0:
|
||||
reporter.FormatError(u8"Incomplete level: there is no object grouped into %s group.",
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PE_LEVELENDE).c_str());
|
||||
break;
|
||||
case 1:
|
||||
// OK. Do nothing.
|
||||
break;
|
||||
default:
|
||||
reporter.FormatError(u8"Bad level: there are more than one objects grouped into %s group.",
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PE_LEVELENDE).c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto* check_points = Shared::Utility::FetchGroup(ctx, Shared::Name::Group::PC_CHECKPOINTS);
|
||||
if (check_points == nullptr) {
|
||||
reporter.FormatWarning(u8"Can not find %s group. This will cause bad render of particle at the level start point.",
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PC_CHECKPOINTS).c_str());
|
||||
}
|
||||
|
||||
auto* reset_points = Shared::Utility::FetchGroup(ctx, Shared::Name::Group::PR_RESETPOINTS);
|
||||
if (reset_points == nullptr) {
|
||||
reporter.FormatError(u8"Incomplete level: can not find %s group.",
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PR_RESETPOINTS).c_str());
|
||||
} else {
|
||||
if (reset_points->GetObjectCount() == 0) {
|
||||
reporter.FormatError(u8"Incomplete level: there is no object grouped into %s group.",
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PR_RESETPOINTS).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
auto sector1_name = builder.get_name(1);
|
||||
auto* sector1 = Shared::Utility::FetchGroup(ctx, sector1_name.c_str());
|
||||
if (sector1 == nullptr) {
|
||||
reporter.FormatError(u8"Incomplete level: can not find %s group.", Shared::Utility::QuoteText(sector1_name).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Ruleset
|
||||
64
Ballance/BMapInspector/Ruleset/ZZQRules.hpp
Normal file
64
Ballance/BMapInspector/Ruleset/ZZQRules.hpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
#include "../Rule.hpp"
|
||||
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
/**
|
||||
* @brief ZZQ Rule 1
|
||||
* @details
|
||||
* Only the first object grouped into "Phys_FloorStopper" can make sound in game.
|
||||
* So it would be better to make "Phys_FloorStopper" only have one item.
|
||||
*
|
||||
* At the same time, show which object is the first object in "Phys_FloorStopper"
|
||||
* to know which object can make sound in game,
|
||||
* if mapper require the stopper which can not make sound them by design.
|
||||
*/
|
||||
class ZZQRule1 : public Rule::IRule {
|
||||
public:
|
||||
ZZQRule1();
|
||||
virtual ~ZZQRule1();
|
||||
YYCC_DELETE_COPY_MOVE(ZZQRule1)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief ZZQ Rule 2
|
||||
* @details
|
||||
* The Ballance should only be included only one group.
|
||||
* This rule will check whether there is intersection between different sector group.
|
||||
*/
|
||||
class ZZQRule2 : public Rule::IRule {
|
||||
public:
|
||||
ZZQRule2();
|
||||
virtual ~ZZQRule2();
|
||||
YYCC_DELETE_COPY_MOVE(ZZQRule2)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief ZZQ Rule 3
|
||||
* @details
|
||||
* A minimalist level must contains following items:
|
||||
* \li One start point.
|
||||
* \li One end point (spaceship).
|
||||
* \li One reset point.
|
||||
* \li "Sector_01" group.
|
||||
*/
|
||||
class ZZQRule3 : public Rule::IRule {
|
||||
public:
|
||||
ZZQRule3();
|
||||
virtual ~ZZQRule3();
|
||||
YYCC_DELETE_COPY_MOVE(ZZQRule3)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -16,6 +16,11 @@ namespace BMapInspector::Utils {
|
||||
Info = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Parse given string as report level.
|
||||
* @param[in] value The string for parsing.
|
||||
* @return Parsed level or nothing (error occurs).
|
||||
*/
|
||||
std::optional<ReportLevel> ParseReportLevel(const std::u8string_view& value);
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,35 @@
|
||||
# How to contribute to LibCmo21
|
||||
|
||||
## Contributor Limit
|
||||
## Contributor Limitation
|
||||
|
||||
This project does not allow contributions coming from employees who are or have been employees of Dassault.
|
||||
|
||||
## Feature Limitation
|
||||
|
||||
For the content of contribution, not all contribution will be accepted.
|
||||
Just like I said, we create **Minimalist** Virtools Environment.
|
||||
The basic RW functions is enough.
|
||||
We do not accept complex function implementations.
|
||||
There are 3 lists which indicate our accept guideline.
|
||||
|
||||
### Wanted Features
|
||||
|
||||
These features will be accepted as soon as possible.
|
||||
|
||||
* The bug fix of any existing code.
|
||||
|
||||
### Not Urgent Features
|
||||
|
||||
These features are in plan, but not urge to merge.
|
||||
|
||||
* The `CK_ID` remap system of Reader.
|
||||
* CK3dEntity hierarchy system.
|
||||
* Other CK classes implementations.
|
||||
* Non-Virtools 2.1 implementations.
|
||||
|
||||
### Don't Care Features
|
||||
|
||||
These features explicitly will not be merged.
|
||||
|
||||
* Run Virtools file.
|
||||
* Plugin system.
|
||||
|
||||
17
DEVNOTE.md
17
DEVNOTE.md
@@ -2,6 +2,15 @@
|
||||
|
||||
This article tells the details of this project for the developer of this project.
|
||||
|
||||
## Version
|
||||
|
||||
When bumping a new version, you should update the version number in following files:
|
||||
|
||||
* `CMakeLists.txt`: It control the version of `LibCmo`, `Unvirt`, `BMap` and `BMapInspector`. All of these projects share the same version.
|
||||
* `Assets/BMapBindings/pybmap/pyproject.toml`: The version of `BMap` Python binding. It should have the same version with `BMap` but not compelled.
|
||||
* `Assets/BMapBindings/BMapSharp/BMapSharp/BMapSharp.csproj`: The version of `BMap` C# binding. Same as above.
|
||||
* `Assets/BMapBindings/bmap-rs/Cargo.toml`: The version of `BMap` Rust binding. Same as above.
|
||||
|
||||
## Java
|
||||
|
||||
### Java
|
||||
@@ -41,8 +50,12 @@ It would be okey for you to use any Antlr you like.
|
||||
|
||||
### Passing Dependencies
|
||||
|
||||
When executing Java code relying on these dependencies,
|
||||
you can use `-cp` option of Java runtime to pass the directory where you can find those dependency's JAR files.
|
||||
When compiling or executing Java code relying on these dependencies,
|
||||
you can use `-cp` option to pass these dependency's JAR files.
|
||||
All dependencies should be specified in one `-cp` option with `;` as separator.
|
||||
|
||||
For example, when compiling, you can pass `-cp "<path-to-antlr4-jar>;<path-to-gson-jar>"` as option.
|
||||
Or pass `-cp ".;<path-to-antlr4-jar>;<path-to-gson-jar>"` when executing.
|
||||
|
||||
## Python
|
||||
|
||||
|
||||
@@ -34,3 +34,8 @@ add_custom_target (LibCmoDocuments
|
||||
install (DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html
|
||||
DESTINATION ${NEMO_INSTALL_DOC_PATH}
|
||||
)
|
||||
|
||||
# Install UNVIRT.md documentation
|
||||
install(FILES ${CMAKE_CURRENT_LIST_DIR}/UNVIRT.md
|
||||
DESTINATION ${NEMO_INSTALL_DOC_PATH}
|
||||
)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user