mirror of
https://github.com/sampletext32/ParkanPlayground.git
synced 2026-08-15 15:07:48 +04:00
BAS file parsing
This commit is contained in:
@@ -0,0 +1,86 @@
|
|||||||
|
using Common;
|
||||||
|
|
||||||
|
namespace ParkanPlayground;
|
||||||
|
|
||||||
|
public record BasContours(InnerContour[] Inner, OuterContour[] Outer);
|
||||||
|
public record InnerContour(Vector3[] Points, uint[] Nums1, uint[] Nums2);
|
||||||
|
public record OuterContour(Vector3[] Points);
|
||||||
|
|
||||||
|
public static class BasParser
|
||||||
|
{
|
||||||
|
public static BasContours ReadFile(string path)
|
||||||
|
{
|
||||||
|
using FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
|
|
||||||
|
return ReadFile(fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BasContours ReadFile(Stream fs)
|
||||||
|
{
|
||||||
|
var innerContourCount = fs.ReadUInt32LittleEndian();
|
||||||
|
|
||||||
|
InnerContour[] innerContours = new InnerContour[innerContourCount];
|
||||||
|
|
||||||
|
for (var i = 0; i < innerContourCount; i++)
|
||||||
|
{
|
||||||
|
var pointCount = fs.ReadUInt32LittleEndian();
|
||||||
|
|
||||||
|
Vector3[] points = new Vector3[pointCount + 1];
|
||||||
|
|
||||||
|
for (var j = 0; j < pointCount + 1; j++)
|
||||||
|
{
|
||||||
|
var x = fs.ReadFloatLittleEndian();
|
||||||
|
var y = fs.ReadFloatLittleEndian();
|
||||||
|
var z = fs.ReadFloatLittleEndian();
|
||||||
|
|
||||||
|
points[j] = new Vector3(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint[] nums1 = new uint[pointCount];
|
||||||
|
|
||||||
|
// batch 1
|
||||||
|
for (var j = 0; j < pointCount; j++)
|
||||||
|
{
|
||||||
|
var num = fs.ReadUInt32LittleEndian();
|
||||||
|
nums1[j] = num;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint[] nums2 = new uint[pointCount];
|
||||||
|
|
||||||
|
// batch 2
|
||||||
|
for (var j = 0; j < pointCount; j++)
|
||||||
|
{
|
||||||
|
var num = fs.ReadUInt32LittleEndian();
|
||||||
|
nums2[j] = num;
|
||||||
|
}
|
||||||
|
|
||||||
|
innerContours[i] = new InnerContour(points, nums1, nums2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var outerContourCount = fs.ReadUInt32LittleEndian();
|
||||||
|
|
||||||
|
OuterContour[] outerContours = new OuterContour[outerContourCount];
|
||||||
|
|
||||||
|
for (var i = 0; i < outerContourCount; i++)
|
||||||
|
{
|
||||||
|
var pointCount = fs.ReadUInt32LittleEndian();
|
||||||
|
|
||||||
|
Vector3[] points = new Vector3[pointCount + 1];
|
||||||
|
for (var j = 0; j < pointCount + 1; j++)
|
||||||
|
{
|
||||||
|
var x = fs.ReadFloatLittleEndian();
|
||||||
|
var y = fs.ReadFloatLittleEndian();
|
||||||
|
var z = fs.ReadFloatLittleEndian();
|
||||||
|
|
||||||
|
points[j] = new Vector3(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
outerContours[i] = new OuterContour(points);
|
||||||
|
}
|
||||||
|
|
||||||
|
BasContours contours = new BasContours(innerContours, outerContours);
|
||||||
|
|
||||||
|
return contours;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,63 +1,3 @@
|
|||||||
using ResTreeLib;
|
using Common;
|
||||||
|
|
||||||
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
|
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
|
||||||
var trfPath = Directory.EnumerateFiles(@"C:\Program Files (x86)\Nikita\Iron Strategy\MISSIONS\SCRIPTS", "*.trf").First();
|
|
||||||
var nodes = ResTreeParser.Parse(trfPath);
|
|
||||||
|
|
||||||
var active = nodes.Where(n => !string.IsNullOrWhiteSpace(n.ShortName) && (n.Node.ResearchCost > 0 || n.Node.ResearchTime > 0)).ToList();
|
|
||||||
Console.WriteLine($"active={active.Count}");
|
|
||||||
|
|
||||||
Analyze("X from Cost", active.Select(n => (A:n.Node.ResearchCost, B:n.Node.ViewPosX, Name:n.ShortName, Idx:n.Index)).ToList());
|
|
||||||
Analyze("Y from Time", active.Select(n => (A:n.Node.ResearchTime, B:n.Node.ViewPosY, Name:n.ShortName, Idx:n.Index)).ToList());
|
|
||||||
|
|
||||||
Console.WriteLine();
|
|
||||||
Console.WriteLine("Largest |Y-Time| (active):");
|
|
||||||
foreach (var n in active.OrderByDescending(n => Math.Abs(n.Node.ViewPosY - n.Node.ResearchTime)).Take(20))
|
|
||||||
{
|
|
||||||
Console.WriteLine($"idx={n.Index,3} {n.ShortName,-12} T={n.Node.ResearchTime,6:0.##} Y={n.Node.ViewPosY,6:0.##} d={n.Node.ViewPosY-n.Node.ResearchTime,6:0.##}");
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine();
|
|
||||||
Console.WriteLine("Largest |X-Cost| (active):");
|
|
||||||
foreach (var n in active.OrderByDescending(n => Math.Abs(n.Node.ViewPosX - n.Node.ResearchCost)).Take(20))
|
|
||||||
{
|
|
||||||
Console.WriteLine($"idx={n.Index,3} {n.ShortName,-12} C={n.Node.ResearchCost,6:0.##} X={n.Node.ViewPosX,6:0.##} d={n.Node.ViewPosX-n.Node.ResearchCost,6:0.##}");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Analyze(string title, List<(float A, float B, string Name, int Idx)> data)
|
|
||||||
{
|
|
||||||
float meanA = data.Average(x => x.A);
|
|
||||||
float meanB = data.Average(x => x.B);
|
|
||||||
float cov=0, varA=0;
|
|
||||||
foreach (var d in data)
|
|
||||||
{
|
|
||||||
var da = d.A - meanA;
|
|
||||||
var db = d.B - meanB;
|
|
||||||
cov += da*db;
|
|
||||||
varA += da*da;
|
|
||||||
}
|
|
||||||
float slope = varA == 0 ? 0 : cov / varA;
|
|
||||||
float intercept = meanB - slope*meanA;
|
|
||||||
|
|
||||||
float mae = data.Average(d => Math.Abs((slope*d.A + intercept) - d.B));
|
|
||||||
float rmse = MathF.Sqrt(data.Average(d => MathF.Pow((slope*d.A + intercept) - d.B,2)));
|
|
||||||
|
|
||||||
float corr = Corr(data.Select(x=>x.A).ToArray(), data.Select(x=>x.B).ToArray());
|
|
||||||
|
|
||||||
Console.WriteLine($"{title}: corr={corr:0.###}, slope={slope:0.###}, intercept={intercept:0.###}, MAE={mae:0.###}, RMSE={rmse:0.###}");
|
|
||||||
}
|
|
||||||
|
|
||||||
static float Corr(float[] a, float[] b)
|
|
||||||
{
|
|
||||||
if (a.Length != b.Length || a.Length == 0) return 0;
|
|
||||||
var ma = a.Average();
|
|
||||||
var mb = b.Average();
|
|
||||||
float top=0, aa=0, bb=0;
|
|
||||||
for(int i=0;i<a.Length;i++)
|
|
||||||
{
|
|
||||||
var da=a[i]-ma; var db=b[i]-mb;
|
|
||||||
top += da*db; aa += da*da; bb += db*db;
|
|
||||||
}
|
|
||||||
if (aa<=0||bb<=0) return 0;
|
|
||||||
return top / MathF.Sqrt(aa*bb);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -198,9 +198,9 @@ IComponent ** LoadSomething(undefined4, undefined4, undefined4, undefined4)
|
|||||||
| 5 | Camera | `terrain.dll LoadCamera` |
|
| 5 | Camera | `terrain.dll LoadCamera` |
|
||||||
| 7 | Atmosphere | `terrain.dll CreateAtmosphere` |
|
| 7 | Atmosphere | `terrain.dll CreateAtmosphere` |
|
||||||
| 9 | Agent | `animesh.dll LoadAgent` |
|
| 9 | Agent | `animesh.dll LoadAgent` |
|
||||||
| 10 | Agent | `animesh.dll LoadAgent` |
|
| 0xa | Agent | `animesh.dll LoadAgent` |
|
||||||
| 11 | Research | `misload.dll LoadResearch` |
|
| 0xb | Research | `misload.dll LoadResearch` |
|
||||||
| 12 | Agent | `animesh.dll LoadAgent` |
|
| 0xc | Agent | `animesh.dll LoadAgent` |
|
||||||
|
|
||||||
Будет дополняться по мере реверса.
|
Будет дополняться по мере реверса.
|
||||||
|
|
||||||
@@ -297,6 +297,30 @@ IComponent ** LoadSomething(undefined4, undefined4, undefined4, undefined4)
|
|||||||
|
|
||||||
Может как-то анимироваться. Как - пока не понятно.
|
Может как-то анимироваться. Как - пока не понятно.
|
||||||
|
|
||||||
|
## `.bas Basement` файл
|
||||||
|
|
||||||
|
Читается в `Terrain.dll/LoadBuilding/CBuilding::ctor`
|
||||||
|
|
||||||
|
Описывает контуры объекта
|
||||||
|
|
||||||
|
Формат
|
||||||
|
```
|
||||||
|
Длина динамическая
|
||||||
|
|
||||||
|
(4 байта) Кол-во внутренних контуров -> IC
|
||||||
|
{
|
||||||
|
(4 байта) Кол-во сегментов контура -> N
|
||||||
|
Vector3[N+1] Точки контура
|
||||||
|
int[N] - непонятно
|
||||||
|
int[N] - тоже непонятно, но выглядит как порядок вершин (e.g. [0 2 1])
|
||||||
|
}[IC]
|
||||||
|
(4 байта) Кол-во внешних контуров -> OC
|
||||||
|
{
|
||||||
|
(4 байта) Кол-во сегментов контура -> N
|
||||||
|
Vector3[N+1] Точки контура
|
||||||
|
}[OC]
|
||||||
|
```
|
||||||
|
|
||||||
## Lightmap (TEXM)
|
## Lightmap (TEXM)
|
||||||
|
|
||||||
У текстур использующихся как карты освещения специальные имена.
|
У текстур использующихся как карты освещения специальные имена.
|
||||||
@@ -309,10 +333,6 @@ IComponent ** LoadSomething(undefined4, undefined4, undefined4, undefined4)
|
|||||||
All lightmaps named whatever.0 → they all end up with no DirectDraw palette attached.
|
All lightmaps named whatever.0 → they all end up with no DirectDraw palette attached.
|
||||||
```
|
```
|
||||||
|
|
||||||
## Люблю разработчиков
|
|
||||||
|
|
||||||
У них получилось сделать LightSource и MaterialDescriptor чётко одинаковыми по размеру (0x5c) из-за чего я потерял 3 дня прыгая по указателям
|
|
||||||
|
|
||||||
## `FXID` - файл эффектов
|
## `FXID` - файл эффектов
|
||||||
|
|
||||||
По сути представляет собой последовательный список саб-эффектов идущих друг за другом.
|
По сути представляет собой последовательный список саб-эффектов идущих друг за другом.
|
||||||
@@ -355,12 +375,6 @@ enum EffectTemplateFlags : uint32_t
|
|||||||
|
|
||||||
# ЕСЛИ ГДЕ-ТО ВИДИШЬ PTR_func_ret1_no_args, ТО ЭТО ShaderConfig
|
# ЕСЛИ ГДЕ-ТО ВИДИШЬ PTR_func_ret1_no_args, ТО ЭТО ShaderConfig
|
||||||
|
|
||||||
В рендеринге порядок дата-стримов такой
|
|
||||||
|
|
||||||
position
|
|
||||||
normal
|
|
||||||
color
|
|
||||||
|
|
||||||
# Внутренняя система ID
|
# Внутренняя система ID
|
||||||
|
|
||||||
- `1` - unknown (implemented by CLandscape) видимо ILandscape
|
- `1` - unknown (implemented by CLandscape) видимо ILandscape
|
||||||
@@ -456,48 +470,6 @@ World3D.dll содержит функцию CreateGameSettings.
|
|||||||
| ---- | 30 (0x1e) | ShadeConfig | | из файла shade.cfg |
|
| ---- | 30 (0x1e) | ShadeConfig | | из файла shade.cfg |
|
||||||
| ---- | (0x8001e) | | | добавляет AniMesh |
|
| ---- | (0x8001e) | | | добавляет AniMesh |
|
||||||
|
|
||||||
|
|
||||||
## Goodies
|
|
||||||
|
|
||||||
```c++
|
|
||||||
// Тип положения объекта в пространстве. Используется при расчёте эффектов,
|
|
||||||
// расстановке объектов и для получения/выдачи их матриц трансформации.
|
|
||||||
enum EPlacementType
|
|
||||||
{
|
|
||||||
// 0: Полная мировая матрица, ориентированная "смотрю на цель".
|
|
||||||
// Входная matrix — это мировая матрица (look-at к какой-то цели).
|
|
||||||
// Движок при необходимости переводит её в локальное/костное пространство
|
|
||||||
// (через inverse(parent/joint) * world и т.п.).
|
|
||||||
// Смысл: "Построй мне мировую матрицу так, чтобы я смотрел на цель;
|
|
||||||
// если я привязан к кости — учти это."
|
|
||||||
Placement_WorldLookAtTarget = 0,
|
|
||||||
|
|
||||||
// 1: Мировая матрица, где основная ось объекта (this->direction)
|
|
||||||
// выровнена по направлению к цели.
|
|
||||||
// Входная matrix — мировая; логика выравнивания использует внутренний
|
|
||||||
// вектор direction и целевую точку/направление.
|
|
||||||
// Смысл: "Используй мой внутренний direction как основную ось
|
|
||||||
// и как можно лучше направь её в сторону цели."
|
|
||||||
Placement_WorldAlignDirectionToTarget = 1,
|
|
||||||
|
|
||||||
// 2: Мировая матрица, полностью задаётся внутренним состоянием объекта.
|
|
||||||
// Входная matrix трактуется как "standalone world" — готовая мировая
|
|
||||||
// матрица объекта. Движок лишь переводит её во внутреннее локальное/
|
|
||||||
// костное пространство (inverse(parent/joint) * world), без look-at
|
|
||||||
// и без выравнивания по цели.
|
|
||||||
// Смысл: "Вот моя конечная мировая матрица. Просто встрои её в иерархию,
|
|
||||||
// не трогая ориентацию дополнительной логикой."
|
|
||||||
Placement_WorldFromStoredTransform = 2,
|
|
||||||
|
|
||||||
// 3: Мировая матрица, ориентированная вдоль вектора движения, но с учётом цели.
|
|
||||||
// Используется предыдущая мировая позиция + текущая + target, чтобы
|
|
||||||
// построить базис: основная ось — направление движения, вспомогательная —
|
|
||||||
// направление к цели.
|
|
||||||
// Смысл: "Ориентируй меня по вектору моего движения, но также учитывай цель."
|
|
||||||
Placement_WorldAlignMotionToTarget = 3
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## Контакты
|
## Контакты
|
||||||
|
|
||||||
Вы можете связаться со мной в [Telegram](https://t.me/bird_egop).
|
Вы можете связаться со мной в [Telegram](https://t.me/bird_egop).
|
||||||
|
|||||||
Reference in New Issue
Block a user