wip map importer stuff
This commit is contained in:
497
Source/Game/MapParser/MapParser.cs
Normal file
497
Source/Game/MapParser/MapParser.cs
Normal file
@@ -0,0 +1,497 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using FlaxEngine;
|
||||
|
||||
// https://web.archive.org/web/20160316213335/http://forums.ubergames.net/topic/2658-understanding-the-quake-3-map-format/
|
||||
// https://web.archive.org/web/20210228125854/https://forums.thedarkmod.com/index.php?/topic/15668-plugin-request-save-map-in-quake-3-format/
|
||||
|
||||
namespace Game
|
||||
{
|
||||
public struct MapFacePlane
|
||||
{
|
||||
public Vector3 v1, v2, v3;
|
||||
public string texture;
|
||||
public Vector2 offset;
|
||||
public float rotation;
|
||||
public Vector2 scale;
|
||||
public int contentFlags, surfaceFlags, surfaceValue;
|
||||
}
|
||||
|
||||
public class MapBrush
|
||||
{
|
||||
public MapFacePlane[] planes;
|
||||
}
|
||||
|
||||
public class MapPatch
|
||||
{
|
||||
public string name;
|
||||
}
|
||||
|
||||
public struct PatchVertex
|
||||
{
|
||||
public Vector3 v;
|
||||
public Vector2 uv;
|
||||
}
|
||||
|
||||
public class MapEntity
|
||||
{
|
||||
public Dictionary<string, string> properties = new Dictionary<string, string>();
|
||||
public List<MapEntity> entities = new List<MapEntity>();
|
||||
public List<MapBrush> brushes = new List<MapBrush>();
|
||||
public List<MapPatch> patches = new List<MapPatch>();
|
||||
}
|
||||
|
||||
public static class MapParser
|
||||
{
|
||||
static public MapEntity Parse(byte[] data)
|
||||
{
|
||||
if (data.Length == 0)
|
||||
return null;
|
||||
|
||||
MapEntity rootEntity = new MapEntity();
|
||||
MapEntity currentEntity = rootEntity;
|
||||
|
||||
int level = 0;
|
||||
int index = 0;
|
||||
//for (int i=0; i<data.Length; i++)
|
||||
do
|
||||
{
|
||||
char c = (char) data[index];
|
||||
char c1 = (index + 1 < data.Length) ? (char) data[index + 1] : (char) 0;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '\n':
|
||||
case '\r':
|
||||
break;
|
||||
case '/':
|
||||
if (c1 == '/')
|
||||
ParseComment(data, ref index);
|
||||
else
|
||||
throw new Exception("unexpected character: '" + c + "'");
|
||||
break;
|
||||
case '{':
|
||||
{
|
||||
currentEntity = new MapEntity();
|
||||
rootEntity.entities.Add(currentEntity);
|
||||
|
||||
level++;
|
||||
|
||||
for (; index < data.Length; index++)
|
||||
{
|
||||
if (data[index] == '\n')
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
|
||||
ParseEntity(currentEntity, data, ref index);
|
||||
break;
|
||||
}
|
||||
case '}':
|
||||
{
|
||||
//currentEntity = rootEntity;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Exception("unsupported character: '" + c + "'");
|
||||
}
|
||||
|
||||
//if (level < 0 || level > 2)
|
||||
// throw new Exception("Failed to parse .map file: unexpected entity found at line " + lineNumber.ToString());
|
||||
} while (++index < data.Length);
|
||||
|
||||
return rootEntity;
|
||||
}
|
||||
|
||||
static void ParseComment(byte[] data, ref int index)
|
||||
{
|
||||
for (; index < data.Length; index++)
|
||||
{
|
||||
if (data[index] == '\n')
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ParseEntity(MapEntity currentEntity, byte[] data, ref int index)
|
||||
{
|
||||
bool entityParsed = false;
|
||||
do
|
||||
{
|
||||
char c = (char) data[index];
|
||||
char c1 = (index + 1 < data.Length) ? (char) data[index + 1] : (char) 0;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '\n':
|
||||
case '\r':
|
||||
break;
|
||||
|
||||
case '/':
|
||||
if (c1 == '/')
|
||||
ParseComment(data, ref index);
|
||||
else
|
||||
throw new Exception("unexpected character: '" + c + "'");
|
||||
break;
|
||||
|
||||
// "name" "value"
|
||||
case '"':
|
||||
{
|
||||
string propName = ParseQuotedString(data, ref index);
|
||||
string propValue = ParseQuotedString(data, ref index);
|
||||
|
||||
if (currentEntity.properties.ContainsKey(propName))
|
||||
throw new Exception("Failed to parse .map file: multiple properties defined for " +
|
||||
propName +
|
||||
" at line ?");// + lineNumber.ToString());
|
||||
currentEntity.properties.Add(propName, propValue);
|
||||
break;
|
||||
}
|
||||
|
||||
// brush
|
||||
case '{':
|
||||
{
|
||||
index++;
|
||||
for (; index < data.Length; index++)
|
||||
{
|
||||
if (data[index] != ' ' && data[index] != '\r' && data[index] != '\n')
|
||||
break;
|
||||
index++;
|
||||
}
|
||||
if (index >= data.Length)
|
||||
break;
|
||||
|
||||
if (data[index] == '(')
|
||||
currentEntity.brushes.Add(ParseBrush(data, ref index));
|
||||
else if (char.IsLetter((char)data[index]) || char.IsNumber((char)data[index]))
|
||||
currentEntity.patches.Add(ParsePatch(data, ref index));
|
||||
break;
|
||||
}
|
||||
case '}':
|
||||
{
|
||||
entityParsed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Exception("unsupported character: '" + c + "'");
|
||||
}
|
||||
} while (index++ < data.Length && !entityParsed);
|
||||
}
|
||||
|
||||
static string ParseQuotedString(byte[] data, ref int index)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
index++;
|
||||
|
||||
for (; index < data.Length; index++)
|
||||
{
|
||||
if (data[index] == '"')
|
||||
break;
|
||||
sb.Append((char)data[index]);
|
||||
}
|
||||
index++;
|
||||
|
||||
while (index < data.Length)
|
||||
{
|
||||
if (data[index] != ' ')
|
||||
break;
|
||||
index++;
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
static string ParseString(byte[] data, ref int index)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (; index < data.Length; index++)
|
||||
{
|
||||
if (data[index] == ' ' || data[index] == '\r' || data[index] == '\n')
|
||||
break;
|
||||
sb.Append((char)data[index]);
|
||||
}
|
||||
index++;
|
||||
|
||||
while (index < data.Length)
|
||||
{
|
||||
if (data[index] != ' ')
|
||||
break;
|
||||
index++;
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
static float ParseFloat(byte[] data, ref int index)
|
||||
{
|
||||
string fs = ParseString(data, ref index);
|
||||
|
||||
if (float.TryParse(fs, NumberStyles.Float, CultureInfo.InvariantCulture, out float value))
|
||||
return value;
|
||||
//else if (float.TryParse(fs, CultureInfo.InvariantCulture, out intValue))
|
||||
// return intValue;
|
||||
else
|
||||
throw new Exception("failed to ParseFloat: " + fs);
|
||||
}
|
||||
|
||||
static int ParseInt(byte[] data, ref int index)
|
||||
{
|
||||
string fs = ParseString(data, ref index);
|
||||
|
||||
if (int.TryParse(fs, out int value))
|
||||
return value;
|
||||
else
|
||||
throw new Exception("failed to ParseInt: " + fs);
|
||||
}
|
||||
|
||||
static Vector3 ParseVector3(byte[] data, ref int index)
|
||||
{
|
||||
return new Vector3(
|
||||
ParseFloat(data, ref index),
|
||||
ParseFloat(data, ref index),
|
||||
ParseFloat(data, ref index)
|
||||
);
|
||||
}
|
||||
|
||||
static Vector2 ParseVector2(byte[] data, ref int index)
|
||||
{
|
||||
return new Vector2(
|
||||
ParseFloat(data, ref index),
|
||||
ParseFloat(data, ref index)
|
||||
);
|
||||
}
|
||||
|
||||
static Vector3 ParsePlaneVector3(byte[] data, ref int index)
|
||||
{
|
||||
index++;
|
||||
while (index < data.Length)
|
||||
{
|
||||
if (data[index] != ' ')
|
||||
break;
|
||||
index++;
|
||||
}
|
||||
|
||||
Vector3 vector = ParseVector3(data, ref index);
|
||||
|
||||
while (index < data.Length)
|
||||
{
|
||||
if (data[index] == ')')
|
||||
break;
|
||||
index++;
|
||||
}
|
||||
|
||||
index++;
|
||||
while (index < data.Length)
|
||||
{
|
||||
if (data[index] != ' ')
|
||||
break;
|
||||
index++;
|
||||
}
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
static MapBrush ParseBrush(byte[] data, ref int index)
|
||||
{
|
||||
MapBrush brush = new MapBrush();
|
||||
|
||||
List<MapFacePlane> planes = new List<MapFacePlane>(6);
|
||||
|
||||
bool brushParsed = false;
|
||||
do
|
||||
{
|
||||
char c = (char) data[index];
|
||||
//char c1 = (index + 1 < data.Length) ? (char) data[index + 1] : (char) 0;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '\r':
|
||||
case '\n':
|
||||
break;
|
||||
|
||||
// brush face
|
||||
// ( <v1> ) ( <v2> ) ( <v3> ) <shader> <x_shift> <y_shift> <rotation> <x_scale> <y_scale> <content_flags> <surface_flags> <value>
|
||||
case '(':
|
||||
{
|
||||
MapFacePlane plane;
|
||||
plane.v1 = ParsePlaneVector3(data, ref index);
|
||||
plane.v2 = ParsePlaneVector3(data, ref index);
|
||||
plane.v3 = ParsePlaneVector3(data, ref index);
|
||||
plane.texture = ParseString(data, ref index);
|
||||
plane.offset = ParseVector2(data, ref index);
|
||||
plane.rotation = ParseFloat(data, ref index);
|
||||
plane.scale = ParseVector2(data, ref index);
|
||||
plane.contentFlags = ParseInt(data, ref index);
|
||||
plane.surfaceFlags = ParseInt(data, ref index);
|
||||
plane.surfaceValue = ParseInt(data, ref index);
|
||||
|
||||
planes.Add(plane);
|
||||
break;
|
||||
}
|
||||
|
||||
case '}':
|
||||
{
|
||||
brushParsed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
if (char.IsLetter(c) || char.IsNumber(c))
|
||||
{
|
||||
// patch name
|
||||
|
||||
}
|
||||
throw new Exception("unsupported character: '" + c + "'");
|
||||
}
|
||||
} while (index++ < data.Length && !brushParsed);
|
||||
|
||||
brush.planes = planes.ToArray();
|
||||
|
||||
return brush;
|
||||
}
|
||||
|
||||
static MapPatch ParsePatch(byte[] data, ref int index)
|
||||
{
|
||||
MapPatch patch = new MapPatch();
|
||||
|
||||
patch.name = ParseString(data, ref index);
|
||||
|
||||
bool patchParsed = false;
|
||||
do
|
||||
{
|
||||
char c = (char) data[index];
|
||||
//char c1 = (index + 1 < data.Length) ? (char) data[index + 1] : (char) 0;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '\r':
|
||||
case '\n':
|
||||
break;
|
||||
|
||||
case '{':
|
||||
{
|
||||
index++;
|
||||
ParsePatchInner(patch, data, ref index);
|
||||
break;
|
||||
}
|
||||
|
||||
case '}':
|
||||
{
|
||||
patchParsed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Exception("unsupported character: '" + c + "'");
|
||||
}
|
||||
} while (index++ < data.Length && !patchParsed);
|
||||
|
||||
return patch;
|
||||
}
|
||||
|
||||
// unfinished and untested
|
||||
static void ParsePatchInner(MapPatch patch, byte[] data, ref int index)
|
||||
{
|
||||
string shaderName = ParseString(data, ref index);
|
||||
|
||||
while (index < data.Length)
|
||||
{
|
||||
if (data[index] == '(')
|
||||
break;
|
||||
index++;
|
||||
}
|
||||
index++;
|
||||
|
||||
int width = ParseInt(data, ref index);
|
||||
int height = ParseInt(data, ref index);
|
||||
int dummy1 = ParseInt(data, ref index);
|
||||
int dummy2 = ParseInt(data, ref index);
|
||||
int dummy3 = ParseInt(data, ref index);
|
||||
|
||||
while (index < data.Length)
|
||||
{
|
||||
if (data[index] == ')')
|
||||
break;
|
||||
index++;
|
||||
}
|
||||
index++;
|
||||
|
||||
while (index < data.Length)
|
||||
{
|
||||
if (data[index] == '(')
|
||||
break;
|
||||
index++;
|
||||
}
|
||||
index++;
|
||||
|
||||
PatchVertex[] vertices = new PatchVertex[width * height];
|
||||
|
||||
bool verticesParsed = false;
|
||||
int vertexIndex = 0;
|
||||
do
|
||||
{
|
||||
char c = (char) data[index];
|
||||
//char c1 = (index + 1 < data.Length) ? (char) data[index + 1] : (char) 0;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '\r':
|
||||
case '\n':
|
||||
break;
|
||||
|
||||
case '(':
|
||||
{
|
||||
index++;
|
||||
|
||||
for (int iw = 0; iw < width; iw++)
|
||||
{
|
||||
while (index < data.Length)
|
||||
{
|
||||
if (data[index] == '(')
|
||||
break;
|
||||
index++;
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
while (index < data.Length)
|
||||
{
|
||||
if (data[index] != ' ')
|
||||
break;
|
||||
index++;
|
||||
}
|
||||
|
||||
vertices[vertexIndex].v = ParseVector3(data, ref index);
|
||||
vertices[vertexIndex].uv = new Vector2(ParseFloat(data, ref index), ParseFloat(data, ref index));
|
||||
vertexIndex++;
|
||||
|
||||
while (index < data.Length)
|
||||
{
|
||||
if (data[index] == ')')
|
||||
break;
|
||||
index++;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case '}':
|
||||
{
|
||||
verticesParsed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Exception("unsupported character: '" + c + "'");
|
||||
}
|
||||
} while (index++ < data.Length && !verticesParsed);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user