From d94a2544a0fca7dcc5f1042c82620468a9ec74f1 Mon Sep 17 00:00:00 2001 From: GoaLitiuM Date: Mon, 5 Jul 2021 15:52:51 +0300 Subject: [PATCH] wip map importer stuff --- Assets/Maps/cube.map | 15 + Content/GameSettings.json | 4 +- Content/Materials/dev/dev_128.flax | Bin 30639 -> 30639 bytes Content/Materials/dev/dev_128_lit.flax | Bin 37368 -> 37368 bytes Content/SceneData/Scene 0/CSG_Collision.flax | Bin 0 -> 1022 bytes Content/SceneData/Scene 0/CSG_Data.flax | Bin 0 -> 1170 bytes Content/SceneData/Scene 0/CSG_Mesh.flax | Bin 0 -> 1660 bytes Content/Scenes/Scene 0.scene | 386 ++++++ Content/Settings/WindowsPlatformSettings.json | 13 +- Content/Textures/dev/dev_128_gray.flax | Bin 1399048 -> 1399062 bytes Content/aerowalk/dev dev_128_gray.flax | Bin 30066 -> 30066 bytes .../Cabrito/Console/ConsoleContentTextBox.cs | 14 +- Source/Game/Cabrito/Console/ConsoleScript.cs | 27 +- Source/Game/MapParser/MapParser.cs | 497 ++++++++ Source/Game/Q3MapImporter.cs | 1074 ++++++++++++++++- Source/GameTarget.Build.cs | 2 + copydll.bat | 4 + launchbg.bat | 4 + 18 files changed, 1969 insertions(+), 71 deletions(-) create mode 100644 Assets/Maps/cube.map create mode 100644 Content/SceneData/Scene 0/CSG_Collision.flax create mode 100644 Content/SceneData/Scene 0/CSG_Data.flax create mode 100644 Content/SceneData/Scene 0/CSG_Mesh.flax create mode 100644 Content/Scenes/Scene 0.scene create mode 100644 Source/Game/MapParser/MapParser.cs create mode 100644 copydll.bat create mode 100644 launchbg.bat diff --git a/Assets/Maps/cube.map b/Assets/Maps/cube.map new file mode 100644 index 0000000..8da68d5 --- /dev/null +++ b/Assets/Maps/cube.map @@ -0,0 +1,15 @@ +// Game: Goake +// Format: Quake3 +// entity 0 +{ +"classname" "worldspawn" +// brush 0 +{ +( -64 -64 -16 ) ( -64 -63 -16 ) ( -64 -64 -15 ) __TB_empty 0 0 0 0.0625 0.0625 0 0 0 +( -64 -64 -16 ) ( -64 -64 -15 ) ( -63 -64 -16 ) __TB_empty 0 0 0 0.0625 0.0625 0 0 0 +( -64 -64 -16 ) ( -63 -64 -16 ) ( -64 -63 -16 ) __TB_empty 0 0 0 0.0625 0.0625 0 0 0 +( 64 64 16 ) ( 64 65 16 ) ( 65 64 16 ) __TB_empty 0 0 0 0.0625 0.0625 0 0 0 +( 64 64 16 ) ( 65 64 16 ) ( 64 64 17 ) __TB_empty 0 0 0 0.0625 0.0625 0 0 0 +( 64 64 16 ) ( 64 64 17 ) ( 64 65 16 ) __TB_empty 0 0 0 0.0625 0.0625 0 0 0 +} +} diff --git a/Content/GameSettings.json b/Content/GameSettings.json index ed6548d..e00aac6 100644 --- a/Content/GameSettings.json +++ b/Content/GameSettings.json @@ -1,11 +1,11 @@ { "ID": "3c7bc3854d42f9b1b0fea9ba0d7fa8e9", "TypeName": "FlaxEditor.Content.Settings.GameSettings", - "EngineBuild": 6219, + "EngineBuild": 6221, "Data": { "ProductName": "Goake", "CompanyName": "GoaLitiuM", - "FirstScene": "a0165b834429c74b9d547c88fb3a0de0", + "FirstScene": "194e05f445ece24ec5448d886e1334df", "NoSplashScreen": true, "Time": "a55dc3c04da4ea3744b7f1994565beac", "Audio": "492a33824049ba25a8bdcdb148179e67", diff --git a/Content/Materials/dev/dev_128.flax b/Content/Materials/dev/dev_128.flax index 69d97245949d1b8f174ec45d747aa4170510027a..f3480122b0547ccce37f9f95f08cf0b283e5dd61 100644 GIT binary patch delta 23 fcmZ4go^kzq#tj=78K-XE$k@!xXtUXrwW7aWe7aWe-a6l diff --git a/Content/SceneData/Scene 0/CSG_Collision.flax b/Content/SceneData/Scene 0/CSG_Collision.flax new file mode 100644 index 0000000000000000000000000000000000000000..9758c7e64626cd848dcafe49a056ef508a179c3c GIT binary patch literal 1022 zcmd5(ze~eV5Wbh+wN=*+QY0XBD4n&74QZo-6=@Y+1D0B88)+j5B4!kER>v;7TO1uM z1t)P32mb^6AK1xw7ZZ(kw1Y>!_wHT3eD~#^ZfqD47!HP|)5dMPb5hYS)6)-$tDQ#! zYGA@Xo-sN!0NH0WSqy@ux&!oq3}SvF$SafgEB>j{?>_o@EhJd!^O_YXneXJ>A5 zY1|LgrTVVbv{y}gTg|9y%0I8S%FTvZFJo=NGMB4%b+4cv(wF{VC>;uSs51J(%4Ufb z@}M+f&Qn5;IXlez!}Z{n(f!D=nmq^coLsD?H|oK_@Dp) literal 0 HcmV?d00001 diff --git a/Content/SceneData/Scene 0/CSG_Data.flax b/Content/SceneData/Scene 0/CSG_Data.flax new file mode 100644 index 0000000000000000000000000000000000000000..253c37697b7233f95906ce4ea5cf7cf6d001221c GIT binary patch literal 1170 zcmZ>E3wPsWzyypyAhLFW51G#uoGhAoT@^1D4SEBq=juaOtagD47i49i+G8O=dFm>Pn literal 0 HcmV?d00001 diff --git a/Content/SceneData/Scene 0/CSG_Mesh.flax b/Content/SceneData/Scene 0/CSG_Mesh.flax new file mode 100644 index 0000000000000000000000000000000000000000..7257c38f77950af5cc05ea9dce49108070c0f1c7 GIT binary patch literal 1660 zcmd6nO>YuW6oy|utUz0e-`46lPF;}HG(dy~D#2k8VWRZWI`AqfP^nRn}?wYmN zDWQ5t;(zOR(d?0DIsD6qOMI^-9e~-X+qL3miE`KL z#haBz6*@g(tM9gdUkh}wUd_8YI4*GN14PChDWa1S!ve?QjddF5=~O3^&Vn&wB^=0g$Y zZEg4+d^#7{8+zz|`3y4oHBpmatB%(X!tF#lC+b#e(l4&J6LhgIs_lUnhcJV~@WGD& pX6esh9ziT1gd;eLV>pfzIEhm@jYXWn63*frB%G(NEdXq_|Eq>n@UIT@Vd_mDC75os*iNr*l#>^mI;YF8*^e z7UDlA=iFv*ud6>YG6)MkxE3<|0FCJ0yDpCp-xx4^8g4gn6po`+yzcZpe<%4~as~$I fZbA<7P=F$oz=ASVpb9mpgAEOEpb73va&Zg3O3gH` delta 266 zcmbQXDYRo#=!7k~TbLPuKmbHAY+(k`EKs%xkYG++P# diff --git a/Content/aerowalk/dev dev_128_gray.flax b/Content/aerowalk/dev dev_128_gray.flax index 1e6152f12ce42bd6213841c127c0ccb77ad54249..88dc859a4da78a33c17bb73518f734ec3b225257 100644 GIT binary patch delta 23 fcmezLit*Dc#tj=78K-XE$e6>#XtP<4C8Q7lhb#$P delta 23 fcmezLit*Dc#tj=78K-RC$e6>#XuVmEC8Q7lhZzZ4 diff --git a/Source/Game/Cabrito/Console/ConsoleContentTextBox.cs b/Source/Game/Cabrito/Console/ConsoleContentTextBox.cs index bdab04e..71cece7 100644 --- a/Source/Game/Cabrito/Console/ConsoleContentTextBox.cs +++ b/Source/Game/Cabrito/Console/ConsoleContentTextBox.cs @@ -22,7 +22,15 @@ namespace Cabrito protected TextLayoutOptions _layout; - public FontReference Font; + private FontReference Font; + + public int FontHeight + { + get + { + return Font.GetFont().Height; + } + } public float LineSpacing = 1.0f; @@ -65,12 +73,14 @@ namespace Cabrito { } - public ConsoleContentTextBox(ConsoleInputTextBox inputBox, float x, float y, float width, float height) : base( + public ConsoleContentTextBox(FontReference font, ConsoleInputTextBox inputBox, float x, float y, float width, float height) : base( x, y, width, height) { this.inputBox = inputBox; Height = height; + Font = font; + _layout = TextLayoutOptions.Default; _layout.VerticalAlignment = TextAlignment.Near; _layout.TextWrapping = TextWrapping.WrapChars; diff --git a/Source/Game/Cabrito/Console/ConsoleScript.cs b/Source/Game/Cabrito/Console/ConsoleScript.cs index 528e07b..ba45c38 100644 --- a/Source/Game/Cabrito/Console/ConsoleScript.cs +++ b/Source/Game/Cabrito/Console/ConsoleScript.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using FlaxEditor; using FlaxEngine; +using FlaxEngine.Assertions; using FlaxEngine.GUI; namespace Cabrito @@ -85,13 +86,12 @@ namespace Cabrito if (consoleBox == null) { //consoleBox = new ConsoleContentTextBox(null, 0, 0, consoleSize.X, consoleSize.Y - fontHeight); - consoleBox = new ConsoleContentTextBox(null, 0, 0, 0, 0); + consoleBox = new ConsoleContentTextBox(fontReference, null, 0, 0, 0, 0); consoleBox.SetAnchorPreset(AnchorPresets.HorizontalStretchTop, true); //consoleBox.AnchorMax = new Vector2(1.0f, ConsoleHeight); //consoleBox.Height = consoleSize.Y - fontHeight; - consoleBox.Font = fontReference; //consoleBox.HorizontalAlignment = TextAlignment.Near; //consoleBox.VerticalAlignment = TextAlignment.Near; @@ -113,14 +113,12 @@ namespace Cabrito if (consoleNotifyBox == null) { //consoleBox = new ConsoleContentTextBox(null, 0, 0, consoleSize.X, consoleSize.Y - fontHeight); - consoleNotifyBox = new ConsoleContentTextBox(null, 0, 0, 0, 0); + consoleNotifyBox = new ConsoleContentTextBox(fontReference, null, 0, 0, 0, 0); consoleNotifyBox.HeightMultiplier = 0; consoleNotifyBox.Height = ConsoleNotifyLines * fontHeight; consoleNotifyBox.SetAnchorPreset(AnchorPresets.HorizontalStretchTop, true); //consoleBox.AnchorMax = new Vector2(1.0f, ConsoleHeight); - consoleNotifyBox.Font = fontReference; - //consoleBox.HorizontalAlignment = TextAlignment.Near; //consoleBox.VerticalAlignment = TextAlignment.Near; //consoleNotifyBox.HeightMultiplier = ConsoleHeight; @@ -263,11 +261,23 @@ namespace Cabrito private void OnSendExceptionLog(Exception exception, FlaxEngine.Object obj) { - Console.Print("[EXCEP] " + exception.Message); + AssertionException assert = exception as AssertionException; + if (assert != null) + { + var assertLines = assert.Message.Split('\n'); + if (assertLines.Length > 2) + Console.Print("Assert Failure: " + assertLines[2]); + else + Console.Print("Assert Failure: " + assert.Message); + } + else + Console.Print("[EXCEP] " + exception.Message); } public override void OnDestroy() { + base.OnDestroy(); + //consoleInputEvent.Triggered -= OnConsoleInputEvent; consoleInputEvent?.Dispose(); consoleBox?.Dispose(); @@ -325,7 +335,6 @@ namespace Cabrito public void OnConsoleClose() { - Console.Print("closed console"); Screen.CursorVisible = false; Screen.CursorLock = CursorLockMode.Locked; @@ -383,7 +392,7 @@ namespace Cabrito } else if (!Console.IsOpen) { - int fontHeight = (int) (consoleNotifyBox.Font.GetFont().Height / Platform.DpiScale); + int fontHeight = (int) (consoleNotifyBox.FontHeight / Platform.DpiScale); if (location.Y < (-conHeight * ConsoleHeight) + fontHeight) { consoleNotifyBox.Visible = true; @@ -395,7 +404,7 @@ namespace Cabrito public void OnPrint(string text) { - int fontHeight = (int) (consoleNotifyBox.Font.GetFont().Height / Platform.DpiScale); + int fontHeight = (int) (consoleNotifyBox.FontHeight / Platform.DpiScale); consoleNotifyBox.Height = Math.Min(ConsoleNotifyLines, Console.Lines.Count) * fontHeight; } diff --git a/Source/Game/MapParser/MapParser.cs b/Source/Game/MapParser/MapParser.cs new file mode 100644 index 0000000..8219d53 --- /dev/null +++ b/Source/Game/MapParser/MapParser.cs @@ -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 properties = new Dictionary(); + public List entities = new List(); + public List brushes = new List(); + public List patches = new List(); + } + + 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 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 planes = new List(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 + // ( ) ( ) ( ) + 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); + } + + } +} \ No newline at end of file diff --git a/Source/Game/Q3MapImporter.cs b/Source/Game/Q3MapImporter.cs index 61a92af..8d22bc7 100644 --- a/Source/Game/Q3MapImporter.cs +++ b/Source/Game/Q3MapImporter.cs @@ -2,91 +2,1055 @@ using System.Collections.Generic; using FlaxEngine; using System.IO; +using System.Linq; +using FlaxEngine.Assertions; +using FlaxEngine.Utilities; +using Console = Cabrito.Console; namespace Game { - public class MapBrush + public struct Edge { + public Vector3 v1, v2; + + public Edge(Vector3 v1, Vector3 v2) + { + this.v1 = v1; + this.v2 = v2; + } + + public static Edge[] GetEdges(Vector3 v1, Vector3 v2, Vector3 v3) + { + return new[] + { + new Edge(v1, v2), + new Edge(v2, v3), + new Edge(v3, v1), + }; + } + + public override bool Equals(object obj) + { + if (obj is Edge) + { + var other = (Edge) obj; + var d1a = Math.Abs((v1 - other.v1).Length); + var d1b = Math.Abs((v1 - other.v2).Length); + var d2a = Math.Abs((v2 - other.v2).Length); + var d2b = Math.Abs((v2 - other.v1).Length); + + var eps = 1f; + if (d1a < eps && d2a < eps) + return true; + else if (d1b < eps && d2b < eps) + return true; + else + return false; + } + + return base.Equals(obj); + } + + public static bool operator ==(Edge edge, object obj) + { + return edge.Equals(obj); + } + + public static bool operator !=(Edge edge, object obj) + { + return !(edge == obj); + } } - public class MapEntity + public class Face { - public Dictionary properties = new Dictionary(); - public List entities = new List(); - public List brushes = new List(); + public Vector3 v1, v2, v3; + public List halfEdges; + public bool visited; + + public Face(Vector3 v1, Vector3 v2, Vector3 v3) + { + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + halfEdges = new List(3); + } + + public Edge[] GetEdges() + { + return new[] + { + new Edge(v1, v2), + new Edge(v2, v3), + new Edge(v3, v1), + }; + } + + public float DistanceToPoint(Vector3 point) + { + Plane plane = new Plane(v1, v2, v3); + + float distance = (point.X * plane.Normal.X) + (point.Y * plane.Normal.Y) + + (point.Z * plane.Normal.Z) + plane.D; + return distance / (float) Math.Sqrt( + (plane.Normal.X * plane.Normal.X) + (plane.Normal.Y * plane.Normal.Y) + + (plane.Normal.Z * plane.Normal.Z)); + } + } + + public struct Tetrahedron + { + public Vector3 v1, v2, v3, v4; + + public Tetrahedron(Vector3 v1, Vector3 v2, Vector3 v3, Vector3 v4) + { + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + this.v4 = v4; + } + + public Face[] GetFaces() + { + return new[] + { + new Face(v1, v2, v3), + new Face(v1, v3, v4), + new Face(v1, v4, v2), + new Face(v2, v4, v3), + }; + } } public class Q3MapImporter : Script { - string mapPath = @"C:\dev\Goake\maps\aerowalk\aerowalk.map"; + private string mapPath = @"C:\dev\GoakeFlax\Assets\Maps\cube.map"; + //private string mapPath = @"C:\dev\Goake\maps\aerowalk\aerowalk.map"; - public override void OnStart() + Model model; + public MaterialBase material; + + const float epsilon = 0.00001f; + + private List FindHull(List points, Edge edge, Vector3 planeNormal) { - string[] lines = File.ReadAllLines(mapPath); + // picks the furthest point of the edge, filter out points from the formed triangle, + // and recursively repeat this until we have only the outermost points of the convex + // hull. - MapEntity rootEntity = new MapEntity(); - MapEntity currentEntity = rootEntity; + if (points.Count == 0) + return new List(); - int level = 0; - uint lineNumber = 0; - foreach (string lineRaw in lines) + // furthest point from edge + Vector3 furthest = Vector3.Zero; + float furthestDist = 0f; + foreach (var vert in points) { - lineNumber++; - string line = lineRaw.TrimStart(); - if (line.StartsWith("//")) + var edgeDir = (edge.v2 - edge.v1).Normalized; + var closest = edge.v1 + edgeDir * Vector3.Dot(vert - edge.v1, edgeDir); + + var dist = (vert - closest).Length; + if (dist > furthestDist) + { + furthest = vert; + furthestDist = dist; + } + } + + List set1 = new List(); + List set2 = new List(); + + var edgeNormal = Vector3.Cross(edge.v2 - edge.v1, planeNormal); + for (int i = 0; i < points.Count; i++) + { + var vert = points[i]; + if (vert == edge.v1 || vert == edge.v2 || vert == furthest) continue; - if (line[0] == '{') - { - level++; + var normal1 = new Plane(vert, edge.v1, edge.v2).Normal; + var normal2 = new Plane(vert, edge.v2, furthest).Normal; + var normal3 = new Plane(vert, furthest, edge.v1).Normal; - if (level == 1) - { - currentEntity = new MapEntity(); - rootEntity.entities.Add(currentEntity); - } - else - { - } + var d1 = Vector3.Dot(planeNormal, normal1); + var d2 = Vector3.Dot(planeNormal, normal2); + var d3 = Vector3.Dot(planeNormal, normal3); + + if ((d1 > 0 && d2 > 0 && d3 > 0) || + (d1 < 0 && d2 < 0 && d3 < 0)) + { + // all triangles formed by these points have similar normals, } - else if (line[0] == '}') + else { - level--; - currentEntity = rootEntity; + // normals pointing different directions, eliminate + continue; } - //if (level < 0 || level > 2) - // throw new Exception("Failed to parse .map file: unexpected entity found at line " + lineNumber.ToString()); - if (line[0] == '"') + var norm = (vert - edge.v1).Normalized; + var dd = Vector3.Dot(edgeNormal.Normalized, norm); + if (dd > epsilon) { - string[] prop = line.Split('\"'); - if (prop.Length != 5) - throw new Exception("Failed to parse .map file: failed to parse property at line " + - lineNumber.ToString()); - - string propName = prop[1]; - string propValue = prop[3]; - - 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); + set1.Add(vert); } - else if (line[0] == '(') + else if (dd < -epsilon) { - //"( -16 302 431 ) ( -16 302 361 ) ( -16 321 361 ) dev/dev_128_gray 0 0 0 0.0625 0.0625 0 0 0" - string[] bru = line.Split(new char[] {'(', ')'}); + set2.Add(vert); + } + } - // TODO: make geometry out of this, use planes? - foreach (var b in bru) + List set = new List(new[] {furthest}); + set.AddRange(FindHull(set1, new Edge(edge.v1, furthest), planeNormal)); + set.AddRange(FindHull(set2, new Edge(furthest, edge.v2), planeNormal)); + return set; + } + + private List QuickHull(List points, Vector3 planeNormal) + { + // longest pair + Vector3 a1 = Vector3.Zero; + Vector3 a2 = Vector3.Zero; + foreach (var v1 in points) + { + foreach (var v2 in points) + { + if ((v2 - v1).LengthSquared > (a2 - a1).LengthSquared) { - //Console.WriteLine(b); + a1 = v1; + a2 = v2; } } } + var edgeNormal = Vector3.Cross(a2 - a1, planeNormal); + List set1 = new List(); + List set2 = new List(); - // Here you can add code that needs to be called when script is created, just before the first game update + foreach (var vert in points) + { + if (vert == a1 || vert == a2) + continue; + + var norm = (vert - a1) /*.Normalized*/; + var dd = Vector3.Dot(edgeNormal /*.Normalized*/, norm); + if (dd > epsilon) + { + set1.Add(vert); + } + else if (dd < -epsilon) + { + set2.Add(vert); + } + else + set1.Add(vert); + } + + List set = new List(new[] {a1, a2}); + set.AddRange(FindHull(set1, new Edge(a2, a1), planeNormal)); + set.AddRange(FindHull(set2, new Edge(a1, a2), planeNormal)); + return set; + } + + private void SortPoints(List points, Vector3 planeNormal) + { + Vector3 center = Vector3.Zero; + foreach (var vert in points) + { + center += vert; + } + + if (points.Count > 0) + center /= points.Count; + + points.Sort((v1, v2) => + { + var dot = Vector3.Dot(planeNormal, Vector3.Cross(v1 - center, v2 - center)); + if (dot > 0) + return 1; + else + return -1; + }); + } + + float PointDistanceFromPlane(Vector3 point, Plane plane) + { + float distance = (point.X * plane.Normal.X) + (point.Y * plane.Normal.Y) + + (point.Z * plane.Normal.Z) + plane.D; + return distance / (float) Math.Sqrt( + (plane.Normal.X * plane.Normal.X) + (plane.Normal.Y * plane.Normal.Y) + + (plane.Normal.Z * plane.Normal.Z)); + } + + private Face[] CreateInitialSimplex(Vector3[] points) + { + // TODO: more optimal to find first set of points which are not coplanar? + + // find the longest edge + Vector3 v1 = Vector3.Zero; + Vector3 v2 = Vector3.Zero; + foreach (var p1 in points) + { + foreach (var p2 in points) + { + if ((p2 - p1).LengthSquared > (v2 - v1).LengthSquared) + { + v1 = p1; + v2 = p2; + } + } + } + + Assert.IsTrue(v1 != v2, "a1 != a2"); + + // find the furthest point from the edge to form a face + Vector3 v3 = Vector3.Zero; + float furthestDist = 0f; + foreach (var point in points) + { + //if (vert == a1 || vert == a2) + // continue; + + var edgeDir = (v2 - v1).Normalized; + var closest = v1 + edgeDir * Vector3.Dot(point - v1, edgeDir); + + var dist = (point - closest).Length; + if (dist > furthestDist) + { + v3 = point; + furthestDist = dist; + } + } + + Assert.IsTrue(v3 != v1, "furthest != a1"); + Assert.IsTrue(v3 != v2, "furthest != a2"); + + // find the furthest point from he face + Plane plane = new Plane(v1, v2, v3); + Vector3 v4 = Vector3.Zero; + float fourthDist = 0f; + foreach (var point in points) + { + if (point == v1 || point == v2 || point == v3) + continue; + + float distance = PointDistanceFromPlane(point, plane); + if (Math.Abs(distance) > fourthDist) + { + v4 = point; + fourthDist = distance; + } + } + + // make sure the tetrahedron is in counter-clockwise order + if (fourthDist > 0) + { + return new Face[] + { + new Face(v1, v3, v2), + new Face(v1, v4, v3), + new Face(v1, v2, v4), + new Face(v2, v3, v4), + }; + } + else + { + return new Face[] + { + new Face(v1, v2, v3), + new Face(v1, v3, v4), + new Face(v1, v4, v2), + new Face(v2, v4, v3), + }; + } + } + + public class HalfEdge + { + public Face face; + //public Face oppositeFace; + public HalfEdge opposite; + public HalfEdge previous, next; + public Edge edge; + //public bool horizonVisited; + + public HalfEdge(Edge edge, Face face) + { + this.edge = edge; + this.face = face; + face.halfEdges.Add(this); + } + } + + //http://algolist.ru/maths/geom/convhull/qhull3d.php + + private List QuickHull2(Vector3[] points) + { + var tetrahedron = CreateInitialSimplex(points); + + List> outsideSet = new List>(); + foreach (var point in points) + { + foreach (Face face in tetrahedron) + { + float distance = face.DistanceToPoint(point); + if (Math.Abs(distance) < epsilon) + { + // point is in the plane, this gets merged + distance = distance; + } + else if (distance > 0) + { + //side.outsideSet.Add(point); + outsideSet.Add(new Tuple(face, point)); + break; + } + } + } + + // all points not in side.outsideSet are inside in "inside" set + + // create half-edges + foreach (var face in tetrahedron) + { + var halfEdges = new List(3); + foreach (var edge in face.GetEdges()) + halfEdges.Add(new HalfEdge(edge, face)); + + for (int i = 0; i < halfEdges.Count; i++) + { + halfEdges[i].previous = halfEdges[(i + 2) % 3]; + halfEdges[i].next = halfEdges[(i + 1) % 3]; + } + } + + // verify + { + var tetrapoints = new List(); + foreach (var face in tetrahedron) + { + foreach (var he in face.halfEdges) + { + if (!tetrapoints.Contains(he.edge.v1)) + tetrapoints.Add(he.edge.v1); + } + } + + foreach (var point in tetrapoints) + { + int foundFaces = 0; + + foreach (var face in tetrahedron) + { + if (face.v1 == point) + foundFaces++; + else if (face.v2 == point) + foundFaces++; + else if (face.v3 == point) + foundFaces++; + } + + Assert.IsTrue(foundFaces == 3, "foundFaces == 3"); + } + } + + + foreach (var face in tetrahedron) + { + Assert.IsTrue(face.halfEdges.Count == 3, "side.halfEdges.Count == 3"); + foreach (var halfEdge in face.halfEdges) + { + bool found = false; + foreach (var otherFace in tetrahedron) + { + if (found) + break; + if (face == otherFace) + continue; + + foreach (var otherHalfEdge in otherFace.halfEdges) + { + if (otherHalfEdge.opposite != null) + continue; + + if (halfEdge.edge == otherHalfEdge.edge) + { + halfEdge.opposite = otherHalfEdge; + otherHalfEdge.opposite = halfEdge; + //halfEdge.oppositeFace = otherFace; + //otherHalfEdge.oppositeFace = face; + found = true; + break; + } + } + } + + Assert.IsTrue(halfEdge.previous != null, "halfEdge.previous != null"); + Assert.IsTrue(halfEdge.next != null, "halfEdge.next != null"); + Assert.IsTrue(halfEdge.opposite != null, "halfEdge.opposite != null"); + //Assert.IsTrue(halfEdge.oppositeFace != null, "halfEdge.oppositeFace != null"); + Assert.IsTrue(halfEdge.opposite.face != null, "halfEdge.opposite.face != null"); + //Assert.IsTrue(halfEdge.oppositeFace == halfEdge.opposite.face, "halfEdge.oppositeFace == halfEdge.opposite.face"); + } + } + + + // grow hull + List> unclaimedPoints2 = new List>(); + List horizonEdges = new List(); + + List hullFaces = new List(); + hullFaces.AddRange(tetrahedron); + + // stop when none of the faces have any visible outside points + int iterCount = 0; + while (outsideSet.Count > 0) + { + iterCount++; + Tuple pointToAdd = null; + Face pointFace = null; + // get furthest point in outside set + /*for (int sideIndex = 0; sideIndex < sides.Count; sideIndex++) + { + TetrahedronSide side = sides[sideIndex]; + if (side.outsideSet.Count == 0) + continue; + + float furthestDist = 0f; + foreach (var point in side.outsideSet) + { + Assert.IsTrue(point != side.face.v1, "point != side.face.v1"); + Assert.IsTrue(point != side.face.v2, "point != side.face.v2"); + Assert.IsTrue(point != side.face.v3, "point != side.face.v3"); + + float distance = PointDistanceFromPlane(point, side.plane); + if (Math.Abs(distance) > furthestDist) + { + pointToAdd = point; + pointSide = side; + furthestDist = distance; + } + } + }*/ + + float furthestDist = 0f; + foreach (var fp in outsideSet) + { + var face = fp.Item1; + var point = fp.Item2; + + float distance = face.DistanceToPoint(point); + if (Math.Abs(distance) > furthestDist) + { + pointToAdd = fp; + pointFace = face; + furthestDist = distance; + } + } + + Assert.IsTrue(pointToAdd != null, "pointToAdd != null"); + + outsideSet.Remove(pointToAdd); + + foreach (var face in hullFaces) + face.visited = false; + + var hullFacesNew = new List(); + + AddPointToHull(pointToAdd.Item2, pointFace, unclaimedPoints2, outsideSet, horizonEdges, hullFacesNew); + + // remove lit/seen/visited faces, their points were added to unclaimed points + for (int i = 0; i < hullFaces.Count; i++) + { + if (hullFaces[i].visited) + { + hullFaces.RemoveAt(i); + i--; + } + } + + hullFaces.AddRange(hullFacesNew); + + + //outsideSet.AddRange(unclaimedPoints2); + //unclaimedPoints2.Clear(); + outsideSet = new List>(unclaimedPoints2); + unclaimedPoints2.Clear(); + + if (iterCount >= 3) + break; + + if (hullFaces.Count > 1000 || iterCount > 1000) + Assert.Fail("overflow"); + } + + List hullPoints = new List(hullFaces.Count * 3); + foreach (var face in hullFaces) + { + hullPoints.Add(face.v1); + hullPoints.Add(face.v2); + hullPoints.Add(face.v3); + } + + return hullPoints; + } + + private void AddPointToHull(Vector3 point, Face face, List> unclaimedPoints2, + List> outsideSet, + List horizonEdges, List hullFaces) + { + horizonEdges.Clear(); + + CalculateHorizon(face, point, unclaimedPoints2, outsideSet, horizonEdges, face.halfEdges[0]); + + // create new faces + if (horizonEdges.Count > 0) + { + List newFaces = new List(); + HalfEdge first = horizonEdges.First(); + HalfEdge prev = null; + foreach (var edge in horizonEdges) + { + var newFace = new Face(point, edge.edge.v1, edge.edge.v2); + var newEdges = new List(); + foreach (var ne in newFace.GetEdges()) + newEdges.Add(new HalfEdge(ne, newFace)); + + for (int i = 0; i < newEdges.Count; i++) + { + newEdges[i].previous = newEdges[(i + 2) % 3]; + newEdges[i].next = newEdges[(i + 1) % 3]; + } + + if (prev != null) + { + var prevEdge = newFaces.Last().halfEdges.Last(); + var lastEdge = newEdges.First(); + lastEdge.opposite = prevEdge; + prevEdge.opposite = lastEdge; + } + + //edge.face = newFace; + + newEdges[1].opposite = edge.opposite; + + newFaces.Add(newFace); + prev = edge; + } + + if (prev != null) + { + var lastEdge = newFaces.Last().halfEdges.Last(); + var firstEdge = newFaces.First().halfEdges.First(); + lastEdge.opposite = firstEdge; + firstEdge.opposite = lastEdge; + //first.previous.opposite = prev.next; + //prev.next.opposite = first.previous; + } + + hullFaces.AddRange(newFaces); + + // verify + foreach (var newFace in hullFaces) + { + Assert.IsTrue(newFace.halfEdges.Count == 3, "AddPointToHull: side.halfEdges.Count == 3"); + foreach (var halfEdge in newFace.halfEdges) + { + /*bool found = false; + foreach (var otherFace in hullFaces) + { + if (found) + break; + if (newFace == otherFace) + continue; + + foreach (var otherHalfEdge in otherFace.halfEdges) + { + if (otherHalfEdge.opposite != null) + continue; + + if (halfEdge.edge == otherHalfEdge.edge) + { + halfEdge.opposite = otherHalfEdge; + otherHalfEdge.opposite = halfEdge; + //halfEdge.oppositeFace = otherFace; + //otherHalfEdge.oppositeFace = face; + found = true; + break; + } + } + }*/ + + Assert.IsTrue(halfEdge.previous != null, "AddPointToHull: halfEdge.previous != null"); + Assert.IsTrue(halfEdge.next != null, "AddPointToHull: halfEdge.next != null"); + Assert.IsTrue(halfEdge.opposite != null, "AddPointToHull: halfEdge.opposite != null"); + //Assert.IsTrue(halfEdge.oppositeFace != null, "halfEdge.oppositeFace != null"); + Assert.IsTrue(halfEdge.opposite.face != null, "AddPointToHull: halfEdge.opposite.face != null"); + //Assert.IsTrue(halfEdge.oppositeFace == halfEdge.opposite.face, "halfEdge.oppositeFace == halfEdge.opposite.face"); + } + } + } + } + + // calculates the outermost edges of the geometry seen from the eyePoint + private void CalculateHorizon(Face face, Vector3 eyePoint, List> unclaimedPoints2, + List> outsideSet, + List horizonEdges, HalfEdge currentEdge) + { + face.visited = true; + + // move outside points of this face to unclaimed points + foreach (var set in outsideSet) + { + if (set.Item1 == face) + unclaimedPoints2.Add(set); + } + + HalfEdge startingEdge = currentEdge; + do + { + Face oppositeFace = currentEdge.opposite.face; + if (!oppositeFace.visited) + { + if (oppositeFace.DistanceToPoint(eyePoint) > epsilon) + { + // positive distance means this is visible + CalculateHorizon(oppositeFace, eyePoint, unclaimedPoints2, outsideSet, horizonEdges, currentEdge.opposite); + } + else + { + if (!horizonEdges.Contains(currentEdge)) + horizonEdges.Add(currentEdge); + } + } + + currentEdge = currentEdge.next; + } while (currentEdge != startingEdge); + } + + public override void OnStart() + { + byte[] mapChars = File.ReadAllBytes(mapPath); + var root = MapParser.Parse(mapChars); + + const float cs = 300f; + + Vector3[] cubePoints = new[] + { + new Vector3(-cs, -cs, -cs), + new Vector3(cs, -cs, -cs), + new Vector3(-cs, cs, -cs), + new Vector3(cs, cs, -cs), + new Vector3(-cs, -cs, cs), + new Vector3(cs, -cs, cs), + new Vector3(-cs, cs, cs), + new Vector3(cs, cs, cs), + }; + + Vector3[] cubeVerts = new[] + { + cubePoints[2], + cubePoints[3], + cubePoints[1], + cubePoints[2], + cubePoints[1], + cubePoints[0], + + cubePoints[0], + cubePoints[1], + cubePoints[4], + cubePoints[1], + cubePoints[5], + cubePoints[4], + + cubePoints[7], + cubePoints[5], + cubePoints[1], + cubePoints[7], + cubePoints[1], + cubePoints[3], + + cubePoints[6], + cubePoints[7], + cubePoints[3], + cubePoints[6], + cubePoints[3], + cubePoints[2], + + cubePoints[4], + cubePoints[6], + cubePoints[2], + cubePoints[4], + cubePoints[2], + cubePoints[0], + + cubePoints[4], + cubePoints[5], + cubePoints[6], + cubePoints[5], + cubePoints[7], + cubePoints[6], + }; + + Vector3[] cubeVerts2 = new[] + { + cubePoints[2], + cubePoints[3], + cubePoints[1], + cubePoints[0], + + cubePoints[0], + cubePoints[1], + cubePoints[4], + cubePoints[5], + + cubePoints[7], + cubePoints[5], + cubePoints[1], + cubePoints[3], + + cubePoints[6], + cubePoints[7], + cubePoints[3], + cubePoints[2], + + cubePoints[4], + cubePoints[6], + cubePoints[2], + cubePoints[0], + + cubePoints[4], + cubePoints[5], + cubePoints[6], + cubePoints[7], + }; + + List vertices = new List(); + + // convert planes + vertices = QuickHull2(cubePoints); + //Assert.IsTrue(vertices.Count == 36, "vertices.Count == 36, count: " + vertices.Count); + if (false) + foreach (var brush in root.entities[0].brushes) + { + List brushVertices = new List(cubeVerts); + //foreach (var plane in new [] { brush.planes.First() }) + foreach (var plane in brush.planes.Skip(0).Take(1)) + //foreach (var plane in brush.planes) + { + Plane p = new Plane(plane.v1, plane.v2, plane.v3); + List clippedVerts = new List(); + List newBrushVertices = new List(); + List faceVertices = new List(); + + if (true) + { + Func isFront = (f) => f > 0; + Func isBack = (f) => f < 0; + + for (int i = 0; i < brushVertices.Count; i++) + { + int i2 = ((i + 1) % 3 == 0) ? (i - 2) : (i + 1); + Vector3 start = brushVertices[i]; + Vector3 end = brushVertices[i2]; + + var d1 = (start.X * p.Normal.X) + (start.Y * p.Normal.Y) + (start.Z * p.Normal.Z) + p.D; + var d2 = (end.X * p.Normal.X) + (end.Y * p.Normal.Y) + (end.Z * p.Normal.Z) + p.D; + + if (isBack(d1)) + { + // include back + faceVertices.Add(start); + } + + if (isBack(d1) && isFront(d2) || isFront(d1) && isBack(d2)) + { + //if (isFront(d2)) + { + // include clip + Ray ray2 = new Ray(start, (end - start).Normalized); + if (p.Intersects(ref ray2, out Vector3 intersect2)) + { + faceVertices.Add(intersect2); + clippedVerts.Add(intersect2); + } + else + d1 = d1; + } + } + + + if (i % 3 == 2) + { + if (faceVertices.Count == 4) + { + // quad, triangulize + /*newBrushVertices.Add(faceVertices[0]); + newBrushVertices.Add(faceVertices[1]); + newBrushVertices.Add(faceVertices[2]); + + newBrushVertices.Add(faceVertices[2]); + newBrushVertices.Add(faceVertices[3]); + newBrushVertices.Add(faceVertices[0]);*/ + + var hullPoints = QuickHull(faceVertices, -p.Normal); + + Assert.IsTrue(hullPoints.Count > 3, "QuickHull reduced quad to triangle!"); + + SortPoints(hullPoints, p.Normal); + + for (int j = 1; j <= hullPoints.Count - 2; j++) + { + Vector3 v1 = hullPoints[0]; + Vector3 v2 = hullPoints[j]; + Vector3 v3 = hullPoints[j + 1]; + + var normal = new Plane(v1, v2, v3).Normal; + if (Vector3.Dot(p.Normal, normal) < 0) + { + v2 = hullPoints[j + 1]; + v3 = hullPoints[j]; + } + + newBrushVertices.Add(v1); + newBrushVertices.Add(v2); + newBrushVertices.Add(v3); + } + } + else if (faceVertices.Count == 3) + { + // ok, either nothing was clipped or two got clipped + newBrushVertices.AddRange(faceVertices); + } + else if (faceVertices.Count == 0) + { + // ok, everything got clipped + d1 = d1; + } + else + d1 = d1; + + faceVertices.Clear(); + } + } + + if (true) + { + List clippedVerts2 = new List(); + foreach (var v in clippedVerts) + { + if (!clippedVerts2.Any(x => (v - x).Length < 0.01)) + clippedVerts2.Add(v); + } + + var hullPoints = QuickHull(clippedVerts2, -p.Normal); + SortPoints(hullPoints, p.Normal); + + for (int i = 1; i <= hullPoints.Count - 2; i++) + { + Vector3 v1 = hullPoints[0]; + Vector3 v2 = hullPoints[i]; + Vector3 v3 = hullPoints[i + 1]; + + var normal = new Plane(v1, v2, v3).Normal; + if (Vector3.Dot(p.Normal, normal) < 0) + { + v2 = hullPoints[i + 1]; + v3 = hullPoints[i]; + } + + newBrushVertices.Add(v1); + newBrushVertices.Add(v2); + newBrushVertices.Add(v3); + } + + /*var brushPoints = QuickHull(newBrushVertices); + newBrushVertices.Clear(); + + for (int i = 1; i <= brushPoints.Count - 2; i++) + { + Vector3 v1 = hullPoints[0]; + Vector3 v2 = hullPoints[i]; + Vector3 v3 = hullPoints[i+1]; + + var normal = new Plane(v1, v2, v3).Normal; + if (Vector3.Dot(p.Normal, normal) < 0) + { + v2 = hullPoints[i + 1]; + v3 = hullPoints[i]; + } + + newBrushVertices.Add(v1); + newBrushVertices.Add(v2); + newBrushVertices.Add(v3); + + //flip = !flip; + }*/ + } + } + + Assert.IsTrue(newBrushVertices.Count % 3 == 0, + "invalid amount of vertices: " + newBrushVertices.Count); + Assert.IsTrue(newBrushVertices.Count > 0, + "brush was clipped completely, vertices: " + newBrushVertices.Count); + + brushVertices.Clear(); + brushVertices.AddRange(newBrushVertices); + Console.Print("plane verts: " + newBrushVertices.Count); + } + + vertices.AddRange(brushVertices); + } + + /*Actor levelActor = Actor.AddChild(); + levelActor.Name = "LevelGeometry"; + + int brushIndex = 1; + foreach (var brush in root.entities[0].brushes) + { + BoxBrush boxBrush = levelActor.AddChild(); + boxBrush.Size = new Vector3(1000f); + boxBrush.Mode = BrushMode.Additive; + boxBrush.Name = "Brush " + brushIndex; + + int planeIndex = 1; + foreach (var brushPlane in brush.planes) + { + Plane plane = new Plane(brushPlane.v1, brushPlane.v2, brushPlane.v3); + + BoxBrush planeBrush = levelActor.AddChild(); + + planeBrush.Mode = BrushMode.Subtractive; + planeBrush.Position = -plane.Normal * plane.D; + planeBrush.Orientation = Quaternion.LookRotation(plane.Normal); + planeBrush.Position = planeBrush.Position - (planeBrush.Transform.Forward * 1500f); + planeBrush.Size = new Vector3(3000f); + planeBrush.Name = "ClipPlane " + brushIndex + " - " + planeIndex; + planeIndex++; + } + + break; + + brushIndex++; + } + + levelActor.Orientation = Quaternion.Euler(90f, 0, 0); + + Scene.BuildCSG(200);*/ + + model = Content.CreateVirtualAsset(); + model.SetupLODs(new int[] {1}); + { + var mesh = model.LODs[0].Meshes[0]; + List triangles = new List(vertices.Count); + for (int i = 0; i < vertices.Count; i++) + triangles.Add(i); + Console.Print("verts: " + vertices.Count); + mesh.UpdateMesh(vertices.ToArray(), triangles.ToArray(), vertices.ToArray()); + } + + StaticModel childModel = Actor.AddChild(); + childModel.Name = "MapModel"; + childModel.Model = model; + childModel.SetMaterial(0, material); } public override void OnEnable() @@ -103,5 +1067,11 @@ namespace Game { // Here you can add code that needs to be called every frame } + + public override void OnDestroy() + { + Destroy(ref model); + base.OnDestroy(); + } } } \ No newline at end of file diff --git a/Source/GameTarget.Build.cs b/Source/GameTarget.Build.cs index bd52f5c..6db75dd 100644 --- a/Source/GameTarget.Build.cs +++ b/Source/GameTarget.Build.cs @@ -7,6 +7,8 @@ public class GameTarget : GameProjectTarget { base.Init(); + //OutputName = "Goake"; + // Reference the modules for game Modules.Add("Game"); //Modules.Add("Cabrito"); diff --git a/copydll.bat b/copydll.bat new file mode 100644 index 0000000..61ac38f --- /dev/null +++ b/copydll.bat @@ -0,0 +1,4 @@ +@echo off +echo copying +copy C:\dev\GoakeFlax\Binaries\GameTarget\Windows\x64\Development\Game.CSharp.* C:\dev\GoakeFlax\Output\WindowsDevelopment\ /Y +echo copied? \ No newline at end of file diff --git a/launchbg.bat b/launchbg.bat new file mode 100644 index 0000000..7731196 --- /dev/null +++ b/launchbg.bat @@ -0,0 +1,4 @@ +@echo off +echo launching +start /B %* +echo launched? \ No newline at end of file