This commit is contained in:
2025-04-03 02:30:16 +08:00
parent 142adb61c6
commit bee7af1732
14907 changed files with 1009130 additions and 13 deletions

View File

@ -0,0 +1,100 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using UnityEngine;
namespace MindPowerSdk
{
/// <summary>
/// 单一贴图层
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MPTileTex
{
public byte TexNo; // 贴图编号
public byte AlphaNo; // Alpha图编号
public override string ToString()
{
return $"{TexNo},{AlphaNo}";
}
public byte SetAlphaNo(byte alphaNo, bool reset = false)
{
if (reset)
{
this.AlphaNo = alphaNo;
}
else
{
this.AlphaNo |= alphaNo;
}
return alphaNo;
}
}
/*
*
MPTileTex TexLayer[4]; // 最多4层重叠
DWORD dwColor; // 左上角第一个顶点的颜色
short sRegion; // 区域属性
BYTE btIsland; // 岛屿属性
BYTE btBlock[4]; // 4个分格的障碍记录
float fHeight; // 左上角第一个顶点的高度
//lemon add@2004.10.18
DWORD dwTColor; // 临时顶点的颜色
DWORD dwXColor; // 混合后的颜色
*
*/
public unsafe struct MPTile
{
public MPTileTex[] TexLayer; // 最多4层重叠
public short Color; // 左上角第一个顶点的颜色
public short Region; // 区域属性
public byte IsLand; // 岛屿属性
public fixed byte Block[4]; // 4个分格的障碍记录
public float Height; // 左上角第一个顶点的高度
public int TColor; // 临时顶点颜色
public int XColor; // 混合后的颜色
public Color UniColor;
public void TileInfo_5To8(byte bt, uint dwNew)
{
byte* pbtTile = stackalloc byte[8];
pbtTile[0] = bt;
pbtTile[1] = 15;
pbtTile[2] = (byte)(dwNew >> 26);
pbtTile[3] = (byte)((dwNew >> 22) & 0x000F);
pbtTile[4] = (byte)((dwNew >> 16) & 63);
pbtTile[5] = (byte)((dwNew >> 12) & 0x000F);
pbtTile[6] = (byte)((dwNew >> 6) & 63);
pbtTile[7] = (byte)((dwNew >> 2) & 0x000F);
this.TexLayer = new MPTileTex[4];
for (int i = 0; i < 8; i += 2)
{
this.TexLayer[i / 2].TexNo = pbtTile[i];
this.TexLayer[i / 2].AlphaNo = pbtTile[i + 1];
}
//Debug.Log($"{TexLayer[0]}|{TexLayer[1]}|{TexLayer[2]}|{TexLayer[3]}");
}
}
public struct MPActiveMapSection
{
public MPTile[] TileData;
public int X, Y; // MapSection所在的位置
public int ActiveTime; // 最后一次使用的时间
public int DataOffset; // 文件数据指针位置 = 0, 表示没有数据
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7c2d672641ccf5748bfcfb41532562a7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,235 @@
using UnityEngine;
namespace MindPowerSdk
{
public partial class MPMap
{
public const float SEA_LEVEL = 0.0f;
private static readonly int[,] Offset =
{
{ 0, 0 },
{ 1, 0 },
{ 0, 1 },
{ 1, 1 }
};
private MPTile GetGroupTile(int x, int y, int no)
{
return GetTile(x + Offset[no, 0], y + Offset[no, 1]);
}
/// <summary>
/// 获取地形的高度
/// </summary>
/// <param name="fX"></param>
/// <param name="fY"></param>
/// <returns></returns>
public float GetHeight(float fX, float fY)
{
// 将浮点坐标转换为整数坐标(取整)
int nX = (int)fX;
int nY = (int)fY;
// 计算四个角点的坐标(构成一个单位矩形)
float fx1 = (float)nX;
float fx2 = (float)nX + 1;
float fy1 = (float)nY;
float fy2 = (float)nY + 1;
// 初始化四个顶点的高度(默认海平面)
float[] fHeight = new float[4] { SEA_LEVEL, SEA_LEVEL, SEA_LEVEL, SEA_LEVEL };
// 获取基准瓦片
MPTile pTile = GetTile(nX, nY);
// 遍历四个顶点获取实际高度
for (int i = 0; i < 4; i++)
{
MPTile pCurTile = GetGroupTile(nX, nY, i);
fHeight[i] = pCurTile.Height;
}
// 构建四个三维顶点
Vector3 v0 = new Vector3(fx1, fy1, fHeight[0]);
Vector3 v1 = new Vector3(fx2, fy1, fHeight[1]);
Vector3 v2 = new Vector3(fx1, fy2, fHeight[2]);
Vector3 v3 = new Vector3(fx2, fy2, fHeight[3]);
// 创建射线(从高处垂直向下)
Vector3 vOrig = new Vector3(fX, fY, 20.0f); // 起点
Vector3 vDir = new Vector3(0, 0, -1); // 方向向下
float u, v; // 用于存储交点参数
Vector3 vPickPos;
// 检测与第一个三角形v0-v1-v2的相交
if (IntersectTri(v0, v1, v2, vOrig, vDir, out u, out v))
{
vPickPos = v0 + u * (v1 - v0) + v * (v2 - v0);
return vPickPos.z; // 返回交点的Z值高度
}
// 检测与第二个三角形v2-v1-v3的相交
if (IntersectTri(v2, v1, v3, vOrig, vDir, out u, out v))
{
vPickPos = v2 + u * (v1 - v2) + v * (v3 - v2);
return vPickPos.z;
}
// 无交点返回0
return 0.0f;
}
// Unity版本的三角形射线相交检测
bool IntersectTri(Vector3 v0, Vector3 v1, Vector3 v2, Vector3 origin, Vector3 direction, out float u, out float v)
{
u = v = 0;
Vector3 edge1 = v1 - v0;
Vector3 edge2 = v2 - v0;
// 计算行列式
Vector3 pvec = Vector3.Cross(direction, edge2);
float det = Vector3.Dot(edge1, pvec);
// 背面剔除det > 0 可改为 det < 0 如果需要双面检测)
if (det < Mathf.Epsilon)
return false;
// 计算U参数并测试范围
Vector3 tvec = origin - v0;
u = Vector3.Dot(tvec, pvec);
if (u < 0 || u > det) return false;
// 计算V参数并测试范围
Vector3 qvec = Vector3.Cross(tvec, edge1);
v = Vector3.Dot(direction, qvec);
if (v < 0 || u + v > det) return false;
// 归一化参数
float invDet = 1.0f / det;
u *= invDet;
v *= invDet;
return true;
}
/// <summary>
/// 返回结束位置与矩形大小
/// </summary>
/// <param name="startX"></param>
/// <param name="startY"></param>
/// <param name="showSize"></param>
/// <returns></returns>
public RectInt GetRect(short startX, short startY, int showSize)
{
int endX = Mathf.Min(startX + showSize, Width);
int endY = Mathf.Min(startY + showSize, Height);
// 本次宽高
int width = endX - startX;
int height = endY - startY;
return new RectInt(endX, endY, width, height);
}
public Mesh GenMesh(short startX, short startY, int showSize)
{
RectInt rect = GetRect(startX, startY, showSize);
// 顶点
int verticesNum = (rect.width) * (rect.height) * 4;
Vector3[] vertices = new Vector3[verticesNum];
int[] triangles = new int[verticesNum * 6];
Vector2[] uv0 = new Vector2[verticesNum];
Vector2[] uv2 = new Vector2[verticesNum];
int vertIdx = 0;
int triIdx = 0;
for (short y = startY; y < rect.y; y++)
{
for (short x = startX; x < rect.x; x++)
{
// 添加4个顶点
for (int i = 0; i < 4; i++)
{
MPTile tile = GetGroupTile(x, y, i);
vertices[vertIdx + i] = new Vector3(x + Offset[i, 0], tile.Height, (y + Offset[i, 1]) * -1);
// Debug.Log($"{x},{y}");
// uv 整个mesh映射到一张贴图上
int rx = x - startX;
// int ry = rect.y - y - 2;
int ry = y - startY + 1;
uv0[vertIdx + i] = new Vector2((rx + Offset[i, 0]) / (float)rect.width, (ry + Offset[i, 1]) * -1 / (float)rect.height);
uv2[vertIdx + i] = new Vector2(Offset[i, 0], Offset[i, 1] * -1);
}
triangles[triIdx + 0] = vertIdx + 2;
triangles[triIdx + 1] = vertIdx + 0;
triangles[triIdx + 2] = vertIdx + 3;
triangles[triIdx + 3] = vertIdx + 0;
triangles[triIdx + 4] = vertIdx + 1;
triangles[triIdx + 5] = vertIdx + 3;
vertIdx += 4;
triIdx += 6;
}
}
Mesh mesh = new Mesh();
mesh.SetVertices(vertices);
mesh.SetTriangles(triangles, 0);
mesh.SetUVs(0, uv0);
mesh.SetUVs(2, uv2);
return mesh;
}
public (Texture2D, Texture2D) GenTxtNoTexture(short startX, short startY, int showSize)
{
RectInt rect = GetRect(startX, startY, showSize);
Texture2D texture1 = new Texture2D(rect.width, rect.height, TextureFormat.RGBA32, false, true); // 贴图值: texNo
texture1.filterMode = FilterMode.Point;
Texture2D texture2 = new Texture2D(rect.width, rect.height, TextureFormat.RGBA32, false, true); // 遮罩值alphaNo
texture2.filterMode = FilterMode.Point;
for (short y = startY; y < rect.y; y++)
{
for (short x = startX; x < rect.x; x++)
{
MPTile tile = GetTile(x, y);
// 4层数据
float r = (tile.TexLayer[0].TexNo - 1) / 64f;
float g = (tile.TexLayer[1].TexNo - 1) / 64f;
float b = (tile.TexLayer[2].TexNo - 1) / 64f;
float a = (tile.TexLayer[3].TexNo - 1) / 64f;
int rx = x - startX;
int ry = rect.y - 1 - y;
texture1.SetPixel(rx, ry, new Color(r, g, b, a));
var alphaNo = new Color(
(tile.TexLayer[0].AlphaNo - 1) / 16f,
(tile.TexLayer[1].AlphaNo - 1) / 16f,
(tile.TexLayer[2].AlphaNo - 1) / 16f,
(tile.TexLayer[3].AlphaNo - 1) / 16f);
texture2.SetPixel(rx, ry, alphaNo);
if (alphaNo.r > 48 || alphaNo.g > 48 || alphaNo.b > 48 || alphaNo.a > 48)
{
Debug.LogError($"{rx},{ry}|{alphaNo}");
}
}
}
texture1.Apply();
texture2.Apply();
return (texture1, texture2);
}
}
}

11
Assets/MindPowerSdk/Map/MPMap.cs.meta generated Normal file
View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 16ac39916dae5c146982e3e6194888e4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,194 @@
using System;
using System.IO;
using UnityEngine;
namespace MindPowerSdk
{
public partial class MPMap
{
private MPMapFileHeader _mapHeader;
public int Width => _mapHeader.Width;
public int Height => _mapHeader.Height;
public int SectionWidth => _mapHeader.SectionWidth;
public int SectionHeight => _mapHeader.SectionHeight;
// 部分数据的数量(地图分块)
public int SectionCntX;
public int SecitonCntY;
public int SectionCnt;
// 地图块索引偏移
private int[] _offsetIdx;
public MPActiveMapSection[,] ActiveSectionArray;
// private List<MPActiveMapSection> ActiveSections = new();
private BinaryReader _fp;
private MPTile _defaultTile;
public MPMap()
{
_defaultTile = new MPTile();
_defaultTile.TexLayer = new MPTileTex[4];
//_pDefaultTile->TexLayer[0].btTexNo = UNDERWATER_TEXNO;
//_pDefaultTile->TexLayer[0].btAlphaNo = 15;
//_pDefaultTile->TexLayer[1].btTexNo = 255; // 标示为DefaultTile
//_pDefaultTile->fHeight = UNDERWATER_HEIGHT;
//_pDefaultTile->dwColor = 0xffffffff;
_defaultTile.TexLayer[0].TexNo = 22;
_defaultTile.TexLayer[0].AlphaNo = 15;
_defaultTile.TexLayer[1].TexNo = 255;
_defaultTile.Height = -2.0f;
}
public void Load(BinaryReader reader)
{
_fp = reader;
this._mapHeader = MPMapFileHeader.Load(reader);
Debug.Log($"version: {this._mapHeader.MapFlag}");
SectionCntX = this.Width / SectionWidth;
SecitonCntY = this.Height / SectionHeight;
SectionCnt = SectionCntX * SecitonCntY;
// 读取块的偏移位置
_offsetIdx = new int[SectionCnt];
for (int i = 0; i < _offsetIdx.Length; i++)
_offsetIdx[i] = reader.ReadInt32();
ActiveSectionArray = new MPActiveMapSection[SectionCntX, SecitonCntY];
FullLoading();
}
private void FullLoading()
{
for (int i = 0; i < SectionCnt; i++)
{
int x = i % SectionCntX;
int y = i / SectionCntX;
if (_offsetIdx[i] != 0)
{
LoadSectionData(x, y);
}
}
}
private void LoadSectionData(int sectionX, int sectionY)
{
MPActiveMapSection section = new MPActiveMapSection
{
X = sectionX,
Y = sectionY
};
LoadSectionData(ref section);
ActiveSectionArray[sectionX, sectionY] = section;
}
/// <summary>
/// 读取地图区块数据
/// </summary>
/// <param name="section"></param>
private void LoadSectionData(ref MPActiveMapSection section)
{
int sectionX = section.X;
int sectionY = section.Y;
section.DataOffset = ReadSectionDataOffset(sectionX, sectionY);
if (section.DataOffset == 0)
{
Debug.LogWarning($"空块数据: {sectionX},{sectionY}");
return;
}
// 开始读取tile数据
_fp.BaseStream.Seek(section.DataOffset, SeekOrigin.Begin);
section.TileData = new MPTile[SectionWidth * SectionHeight];
for (int y = 0; y < SectionHeight; y++)
{
for (int x = 0; x < SectionWidth; x++)
{
int idx = GetMPTileIndex(x, y);
MPTile tile = new MPTile();
SNewFileTile fileTile = SNewFileTile.Load(_fp);
// rgb565 to int
short rgb = fileTile.Color;
byte r = (byte)((rgb & 0xf800) >> 8);
byte g = (byte)((rgb & 0x7e0) >> 3);
byte b = (byte)((rgb & 0x1f) << 3);
tile.UniColor = new Color(r / 255f, g / 255f, b / 255f);
tile.TileInfo_5To8(fileTile.btTileInfo, fileTile.dwTileInfo);
tile.Height = (fileTile.Height) * 10f / 100.0f;
tile.IsLand = fileTile.IsLand;
tile.Region = fileTile.Region;
unsafe
{
Span<byte> dst = new Span<byte>(tile.Block, 4);
Span<byte> src = new Span<byte>(fileTile.Block, 4);
src.CopyTo(dst);
}
section.TileData[idx] = tile;
}
}
}
/// <summary>
/// 设置读取区块数据的偏移
/// </summary>
/// <param name="sectionX"></param>
/// <param name="sectionY"></param>
/// <returns></returns>
private int ReadSectionDataOffset(int sectionX, int sectionY)
{
int loc = sectionY * SectionCntX + sectionX;
return _offsetIdx[loc];
}
public int GetMPTileIndex(int x, int y)
{
return SectionWidth * y + x;
}
/// <summary>
/// 通过地图坐标直接获取Tile点
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public MPTile GetTile(int x, int y)
{
// y = Height - y - 1;
int sX = x / SectionWidth;
int sY = y / SectionHeight;
if (ActiveSectionArray.GetLength(0) <= sX || ActiveSectionArray.GetLength(1) <= sY)
return _defaultTile;
MPActiveMapSection section = ActiveSectionArray[sX, sY];
if (section.TileData is not { Length: > 0 }) return _defaultTile;
int tX = x % SectionWidth;
int tY = y % SectionHeight;
int idx = GetMPTileIndex(tX, tY);
if (idx >= section.TileData.Length || idx < 0)
return _defaultTile;
return section.TileData[idx];
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8d48cf981a8a35d488fa8efbbc1d0e56
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,50 @@
using System.IO;
using System.Runtime.InteropServices;
namespace MindPowerSdk
{
/// <summary>
///
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MPMapFileHeader
{
/// <summary>
/// 类型标识 = 780624
/// </summary>
public int MapFlag { get; private set; }
/// <summary>
/// 地图宽度
/// </summary>
public int Width { get; private set; }
/// <summary>
/// 地图高度
/// </summary>
public int Height { get; private set; }
/// <summary>
/// 每个Section(部分)宽度
/// </summary>
public int SectionWidth { get; private set; }
/// <summary>
/// 每个Section(部分)高度
/// </summary>
public int SectionHeight { get; private set; }
public static MPMapFileHeader Load(BinaryReader reader)
{
MPMapFileHeader header = default;
header.MapFlag = reader.ReadInt32();
header.Width = reader.ReadInt32();
header.Height = reader.ReadInt32();
header.SectionWidth = reader.ReadInt32();
header.SectionHeight = reader.ReadInt32();
return header;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 01c3951dd660b8c4ebdec0bc39a0d925
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,34 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace MindPowerSdk
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe struct SNewFileTile
{
// DWORD dwTileInfo; // 保存3个layer的tex no和alpha no
// BYTE btTileInfo; // 最下层的tex no
// short sColor; // 32bit的颜色保存为565
// char cHeight; // 每10cm为一级来保存高度
// short sRegion;
// BYTE btIsland;
// BYTE btBlock[4];
public uint dwTileInfo;
public byte btTileInfo;
public short Color;
public sbyte Height;
public short Region;
public byte IsLand;
public fixed byte Block[4];
public static SNewFileTile Load(BinaryReader reader)
{
SNewFileTile s = new SNewFileTile();
Span<byte> bytes = new Span<byte>(&s, sizeof(SNewFileTile));
reader.Read(bytes);
return s;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7de4f04a34c974247b83bb95ce85ee20
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,157 @@
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using MindPowerSdk;
using UnityEngine;
namespace MindPowerSdk
{
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct SceneObjInfo
{
public ushort TypeId; // 高2位是type(0: 场景物件, 1: 特效物件), 其余是ID
public int X; // 相对坐标
public int Y;
public short HeightOff;
public short YawAngle;
public short Scale; // 保留未使用
public short GetTypeId()
{
return (short)(TypeId >> 14);
}
public short GetID()
{
return (short)(TypeId & 0x3FFF);
}
public override string ToString()
{
return $"type={GetTypeId()},id={GetID()},x={X},y={Y},height_off={HeightOff},yaw_angle={YawAngle},scale={Scale}";
}
}
public class SceneObjFile
{
/// <summary>
/// 文件头
/// </summary>
public struct Header
{
public string Title; // "HF Object File!" char[16]
public int Version;
public int FileSize;
public int SectionCntX; // 地图的横向区域数
public int SectionCntY; // 地图的纵向区域数
public int SectionWidth; // 区域的宽度单位Tile
public int SectionHeight; // 区域的高度单位Tile
public int SectionObjNum; // 区域允许的最大物件数
}
/// <summary>
/// 用来索引文件中对象的数据
/// </summary>
public struct SSectionIndex
{
public int ObjInfoPos;
public int ObjNum;
}
public Header FileHeader { get; private set; }
SSectionIndex[,] sectionIndex;
public List<SceneObjInfo>[,] objInfos;
public unsafe int Load(string obj_file)
{
FileStream fs = File.OpenRead(obj_file);
BinaryReader r = new BinaryReader(fs);
long size = r.BaseStream.Length;
Header s = new Header();
s.Title = Encoding.ASCII.GetString(r.ReadBytes(16));
s.Version = r.ReadInt32();
s.FileSize = r.ReadInt32(); // 实际是32位
s.SectionCntX = r.ReadInt32();
s.SectionCntY = r.ReadInt32();
s.SectionWidth = r.ReadInt32();
s.SectionHeight = r.ReadInt32();
s.SectionObjNum = r.ReadInt32();
FileHeader = s;
Debug.Log(JsonUtility.ToJson(s));
if (s.FileSize != size)
{
Debug.LogWarning($"{obj_file} 文件szie不匹配: {s.FileSize} != {size}");
return -1;
}
if (s.Version != 600)
{
Debug.LogWarning($"{obj_file} 版本只支持600: 当前是={s.Version}");
return -1;
}
// 读取索引信息
sectionIndex = new SSectionIndex[s.SectionCntX, s.SectionCntY];
for (int y = 0; y < s.SectionCntY; y++)
{
for (int x = 0; x < s.SectionCntX; x++)
{
SSectionIndex data = new SSectionIndex();
data.ObjInfoPos = r.ReadInt32();
data.ObjNum = r.ReadInt32();
sectionIndex[x, y] = data;
if (data.ObjNum != 0)
Debug.Log(JsonUtility.ToJson(data));
}
}
// 读取场景物体
objInfos = new List<SceneObjInfo>[s.SectionCntX, s.SectionCntY];
for (int y = 0; y < s.SectionCntY; y++)
{
for (int x = 0; x < s.SectionCntX; x++)
{
SSectionIndex idx = sectionIndex[x, y];
if (idx.ObjNum == 0)
continue;
var list = objInfos[x, y] ??= new List<SceneObjInfo>();
r.BaseStream.Seek(idx.ObjInfoPos, SeekOrigin.Begin);
for (int i = 0; i < idx.ObjNum; i++)
{
byte[] tmpBytes = r.ReadBytes(sizeof(SceneObjInfo));
SceneObjInfo sceneObjInfo = StructReader.ReadStructFromArray<SceneObjInfo>(tmpBytes);
// 转成绝对坐标
int sectionX = x * s.SectionWidth * 100;
int sectionY = y * s.SectionHeight * 100;
sceneObjInfo.X += sectionX;
sceneObjInfo.Y += sectionY;
list.Add(sceneObjInfo);
Debug.Log(sceneObjInfo.ToString());
}
}
}
return 0;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 74622940af1063046bc3832a354b4566
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: