From 8bde0a7f18cd54fb185b15c108f35ce4d3e288ce Mon Sep 17 00:00:00 2001 From: GoaLitiuM Date: Tue, 31 Aug 2021 20:03:02 +0300 Subject: [PATCH] tests --- .gitignore | 2 + Content/GameSettings.json | 14 +- Content/Scenes/ShadowmapTest.scene | 127 +++----- Source/Game/Cabrito/Console/Console.cs | 9 +- Source/Game/CameraMovement.cs | 1 + Source/Game/Q3MapImporter.cs | 429 +++++++++++++++---------- Source/GoakeTestsTarget.Build.cs | 32 ++ Tests/Cabrito/ConsoleTests.cs | 43 +++ Tests/GoakeTests.csproj | 20 ++ Tests/MapParser/MapParserTests.cs | 74 +++++ lore.md | 19 ++ 11 files changed, 511 insertions(+), 259 deletions(-) create mode 100644 Source/GoakeTestsTarget.Build.cs create mode 100644 Tests/Cabrito/ConsoleTests.cs create mode 100644 Tests/GoakeTests.csproj create mode 100644 Tests/MapParser/MapParserTests.cs create mode 100644 lore.md diff --git a/.gitignore b/.gitignore index fdf91b4..15a75fe 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ Output/* Source/obj/ .idea/* *.DotSettings.user +Tests/bin/ +Tests/obj/ diff --git a/Content/GameSettings.json b/Content/GameSettings.json index 00f834f..bd53768 100644 --- a/Content/GameSettings.json +++ b/Content/GameSettings.json @@ -5,19 +5,31 @@ "Data": { "ProductName": "Goake", "CompanyName": "GoaLitiuM", + "CopyrightNotice": "ddc", + "Icon": null, "FirstScene": "194e05f445ece24ec5448d886e1334df", "NoSplashScreen": true, + "SplashScreen": null, "Time": "a55dc3c04da4ea3744b7f1994565beac", "Audio": "492a33824049ba25a8bdcdb148179e67", "LayersAndTags": "60078f6a489f7c57e47fce83afaada66", "Physics": "4bd8a4cc460399b5f1975fbe0a668e3f", "Input": "8ec53dba4c238bfbea1d62922e612a4d", "Graphics": "f94d5aae457aeba67033a8a4ca753214", + "Navigation": null, + "Localization": null, "GameCooking": "af2e52554f7faed7b4937181dd22d166", "Streaming": "3a6ffc3a43e684f77cfed1a1f8f6bd0e", "CustomSettings": { "BrushMaterials": "29a0b0c54b40eb3e6857ffb4c9cab71e" }, - "WindowsPlatform": "4a5eec97484253fed72934860ae62c40" + "WindowsPlatform": "4a5eec97484253fed72934860ae62c40", + "UWPPlatform": null, + "LinuxPlatform": null, + "PS4Platform": null, + "XboxOnePlatform": null, + "XboxScarlettPlatform": null, + "AndroidPlatform": null, + "SwitchPlatform": null } } \ No newline at end of file diff --git a/Content/Scenes/ShadowmapTest.scene b/Content/Scenes/ShadowmapTest.scene index 7073b1b..c03abeb 100644 --- a/Content/Scenes/ShadowmapTest.scene +++ b/Content/Scenes/ShadowmapTest.scene @@ -1,7 +1,7 @@ { "ID": "0c0af24c4fafa9c72af2c08723321976", "TypeName": "FlaxEngine.SceneAsset", - "EngineBuild": 6219, + "EngineBuild": 6224, "Data": [ { "ID": "0c0af24c4fafa9c72af2c08723321976", @@ -25,103 +25,53 @@ } }, { - "ID": "4ef1f38b4569142b55b0ff935525d47a", - "TypeName": "FlaxEngine.RigidBody", - "ParentID": "0c0af24c4fafa9c72af2c08723321976", - "Name": "PlayerPrefab", - "Transform": { - "Translation": { - "X": -167.25045776367188, - "Y": -10.409317016601563, - "Z": 61.111427307128909 - } - }, - "OverrideMass": true, - "Mass": 10.0, - "LinearDamping": 0.0, - "AngularDamping": 0.0, - "Constraints": 56, - "IsKinematic": true, - "EnableGravity": false + "ID": "c28e12a74ac7d87cf078c6a52bf696e0", + "PrefabID": "2d0e518b47d735c98312dd87cc42d6d7", + "PrefabObjectID": "999a202c47de967d2bdfd09abcc6df4e", + "ParentID": "0c0af24c4fafa9c72af2c08723321976" }, { - "ID": "e3bab01a4912873fc2e9ff855a214bf7", - "TypeName": "Game.PlayerMovement", - "ParentID": "4ef1f38b4569142b55b0ff935525d47a", + "ID": "26ae32e2418882abc15d568692fdd97f", + "PrefabID": "2d0e518b47d735c98312dd87cc42d6d7", + "PrefabObjectID": "b29a58f545071ca393afaab21faa83ad", + "ParentID": "c28e12a74ac7d87cf078c6a52bf696e0", "V": {} }, { - "ID": "08ed03be4d537814ba01edb88f6801e1", - "TypeName": "FlaxEngine.EmptyActor", - "ParentID": "4ef1f38b4569142b55b0ff935525d47a", - "Name": "RootActor" + "ID": "ddcf668e46d6c73d5d0d258822f24594", + "PrefabID": "2d0e518b47d735c98312dd87cc42d6d7", + "PrefabObjectID": "a67383834fc3f6f1106e8099e5557e32", + "ParentID": "c28e12a74ac7d87cf078c6a52bf696e0" }, { - "ID": "499cfa784579b58bba713daa0983d705", - "TypeName": "FlaxEngine.Camera", - "ParentID": "08ed03be4d537814ba01edb88f6801e1", - "Name": "Camera", - "Transform": { - "Translation": { - "X": 0.0, - "Y": 28.7337646484375, - "Z": 0.0 - } - }, - "Near": 1.0 + "ID": "9b502ff34a9bee27cf23aabd9cf1da91", + "PrefabID": "2d0e518b47d735c98312dd87cc42d6d7", + "PrefabObjectID": "b8fc420f4efe96fa2b4042bd98fd3e74", + "ParentID": "ddcf668e46d6c73d5d0d258822f24594" }, { - "ID": "b59df4764594c5ee84a759b5e65bfc5a", - "TypeName": "FlaxEngine.AudioListener", - "ParentID": "08ed03be4d537814ba01edb88f6801e1", - "Name": "AudioListener" + "ID": "7a0c2f7648daf4b52776858dd302d509", + "PrefabID": "2d0e518b47d735c98312dd87cc42d6d7", + "PrefabObjectID": "226bdb004e72848777178a9d1f207cbf", + "ParentID": "9b502ff34a9bee27cf23aabd9cf1da91" }, { - "ID": "fbaa6db24f0a2a9db36bc6a043fc2572", - "TypeName": "FlaxEngine.CapsuleCollider", - "ParentID": "4ef1f38b4569142b55b0ff935525d47a", - "IsActive": false, - "Name": "CapsuleCollider", - "Transform": { - "Orientation": { - "X": 0.0, - "Y": 0.0, - "Z": 0.7071068286895752, - "W": 0.7071068286895752 - } - }, - "ContactOffset": 0.0, - "Material": "d99b8b6f40198beea4703dadd642150e", - "Radius": 16.0, - "Height": 52.0 + "ID": "320c57c5499ef1692820f28954142720", + "PrefabID": "2d0e518b47d735c98312dd87cc42d6d7", + "PrefabObjectID": "8bd80bca49ac35da66aabaa2e473851d", + "ParentID": "c28e12a74ac7d87cf078c6a52bf696e0" }, { - "ID": "9677298d46e5d5d7021f7cb607bd3023", - "TypeName": "FlaxEngine.BoxCollider", - "ParentID": "4ef1f38b4569142b55b0ff935525d47a", - "Name": "BoxCollider", - "ContactOffset": 0.0, - "Material": "ffe0e84c49607480f67a3994a7fe11a8", - "Size": { - "X": 32.0, - "Y": 84.0, - "Z": 32.0 - } + "ID": "deef0a234d3a14fad95b50b822e9f096", + "PrefabID": "2d0e518b47d735c98312dd87cc42d6d7", + "PrefabObjectID": "d9d61d314ad79d49ba08059cef50f802", + "ParentID": "c28e12a74ac7d87cf078c6a52bf696e0" }, { - "ID": "f472490345bbfe282aefa2aa027e0138", - "TypeName": "FlaxEngine.MeshCollider", - "ParentID": "4ef1f38b4569142b55b0ff935525d47a", - "IsActive": false, - "Name": "CylinderCollider", - "Transform": { - "Scale": { - "X": 0.3199999928474426, - "Y": 0.8399999737739563, - "Z": 0.3199999928474426 - } - }, - "CollisionData": "593d92914c4bd54679ddec9e539bba80" + "ID": "fc1310c843e3136b468f83947df3566d", + "PrefabID": "2d0e518b47d735c98312dd87cc42d6d7", + "PrefabObjectID": "b377cc03418270d87e8a5b92cb5374ea", + "ParentID": "c28e12a74ac7d87cf078c6a52bf696e0" }, { "ID": "b5d566374477944e69bdc2b86249883b", @@ -168,13 +118,13 @@ "Transform": { "Translation": { "X": 0.0, - "Y": 733.0, + "Y": 735.0, "Z": 0.0 } }, "Control": "FlaxEngine.GUI.Label", "Data": { - "Text": "uFPS: 120\nrFPS: 120\npFPS: 30\nCon: NaNms\nDirectX11\nGC memory: 15.30202MB", + "Text": "eFPS: 121\nuFPS: 120\nrFPS: 120\npFPS: 30\nCon: NaNms\nDirectX11\nGC memory: 9.235704MB", "TextColor": { "R": 1.0, "G": 1.0, @@ -194,6 +144,7 @@ "Font": "4508d98f4aa1f0bd59362b81d47e38f4", "Size": 10 }, + "Material": null, "Margin": { "Left": 0.0, "Right": 0.0, @@ -222,7 +173,7 @@ "Left": 0.0, "Right": 143.0, "Top": -80.0, - "Bottom": 96.0 + "Bottom": 112.0 }, "Scale": { "X": 1.0, @@ -260,8 +211,8 @@ "Name": "ContainerControl 0", "Transform": { "Translation": { - "X": 45644.0, - "Y": 0.5, + "X": 44861.0, + "Y": 1.5, "Z": 0.0 } }, diff --git a/Source/Game/Cabrito/Console/Console.cs b/Source/Game/Cabrito/Console/Console.cs index 62348fc..82a1ec9 100644 --- a/Source/Game/Cabrito/Console/Console.cs +++ b/Source/Game/Cabrito/Console/Console.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; -using FlaxEngine.Assertions; namespace Cabrito { @@ -32,13 +31,12 @@ namespace Cabrito instance = new ConsoleInstance(); } - private static void Destroy() + public static void Destroy() { if (instance != null) { instance.Dispose(); instance = null; - FlaxEngine.Debug.Log("Console.Destroy"); } } @@ -139,7 +137,10 @@ namespace Cabrito assemblyName.StartsWith("Mono.") || assemblyName == "mscorlib" || assemblyName == "Newtonsoft.Json" || - assemblyName.StartsWith("FlaxEngine.")) + assemblyName.StartsWith("FlaxEngine.") || + assemblyName.StartsWith("JetBrains.") || + assemblyName.StartsWith("Microsoft.") || + assemblyName.StartsWith("nunit.")) { continue; } diff --git a/Source/Game/CameraMovement.cs b/Source/Game/CameraMovement.cs index b6ab060..aa86627 100644 --- a/Source/Game/CameraMovement.cs +++ b/Source/Game/CameraMovement.cs @@ -1,6 +1,7 @@ using FlaxEngine; using Cabrito; using System.Diagnostics; +using System.IO; namespace Game { diff --git a/Source/Game/Q3MapImporter.cs b/Source/Game/Q3MapImporter.cs index 8b355a5..a499cc2 100644 --- a/Source/Game/Q3MapImporter.cs +++ b/Source/Game/Q3MapImporter.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using FlaxEngine; using System.IO; using System.Linq; @@ -1082,6 +1083,16 @@ namespace Game } } + public class BrushGeometry + { + public MapBrush brush; + public Vector3[] vertices; + public uint[] indices; + public Vector2[] uvs; + public Vector3[] normals; + public Model model = null; + public MaterialBase brushMaterial = null; // FIXME: brush can have multiple textures + } public class Q3MapImporter : Script @@ -1103,7 +1114,7 @@ namespace Game - void QuickHull(Vector3[] points, out Vector3[] outVertices) + static void QuickHull(Vector3[] points, out Vector3[] outVertices) { var verts = new List(); var tris = new List(); @@ -1424,7 +1435,7 @@ namespace Game return brushVertices.ToArray(); } - void TriangulateBrush3(MapBrush brush, out Vector3[] vertices) + static public void TriangulateBrush3(MapBrush brush, out Vector3[] vertices) { float cs = 3000f; @@ -1638,13 +1649,13 @@ namespace Game //uniqPoints.Add(new Vector3((float)Math.Round(v.X, 3), (float)Math.Round(v.Y, 3), (float)Math.Round(v.Z, 3))); } - debugPoints = new List(uniqPoints); + //debugPoints = new List(uniqPoints); Vector3[] hullPoints; QuickHull(uniqPoints.ToArray(), out hullPoints); - var ms = new MeshSimplifier(); + var ms = new MeshSimplifier(1f, 7f); var optimizedVerts = ms.Simplify(hullPoints); brushVertices.Clear(); @@ -1652,7 +1663,7 @@ namespace Game } else { - debugPoints = new List(faceVertices); + //debugPoints = new List(faceVertices); var hullPoints = faceVertices; @@ -1669,195 +1680,281 @@ namespace Game vertices = brushVertices.ToArray(); } + /* + Development (game) + cube3: 8.1ms + 123ms + aerowalk: 78ms + 1372ms + + Development (editor) + cube3: 4.6ms + 77.3ms + aerowalk: 74ms + 1328ms + + Release + cube3: 4.4ms + 61.4ms + aerowalk: 17ms + 511ms + + UnitTest release: + aerowalk: 8ms + 267ms + + aero unit: + .net6: 667 + 229 + .net5: 704 + 237 + .net5win7: 697 + 237 + .net48 809 + 242 + .net472 810 + 246 + .net462 810 + 243 + .net452 808 + 244 + */ public override void OnStart() { byte[] mapChars = File.ReadAllBytes(mapPath); + + Stopwatch sw = Stopwatch.StartNew(); root = MapParser.Parse(mapChars); + sw.Stop(); + + Console.Print("Map parsing time: " + sw.Elapsed.TotalMilliseconds + "ms"); bool oneMesh = false; bool convexMesh = true; + if (!oneMesh) { Dictionary materials = null; var mapRootActor = Actor.AddChild(); mapRootActor.Name = "MapRootActor"; + List brushGeometries = new List(root.entities[0].brushes.Count); + + + // pass 1: triangulation + sw.Restart(); int brushIndex = 0; foreach (var brush in root.entities[0].brushes) { try { - List vertices = new List(); - List uvs = new List(); - List normals = new List(); + BrushGeometry geom = new BrushGeometry(); - TriangulateBrush3(brush, out Vector3[] brushVertices); - Vector2[] brushUvs = new Vector2[brushVertices.Length]; - Vector3[] brushNormals = new Vector3[brushVertices.Length]; + TriangulateBrush3(brush, out geom.vertices); + geom.brush = brush; - // FIXME: brush can have multiple textures - MaterialBase brushMaterial; - var textureName = brush.planes[0].texture; - - if (materials == null) - { - var customSettings = Engine.GetCustomSettings("BrushMaterials"); - BrushMaterialList brushMaterialList = customSettings?.CreateInstance(); - if (brushMaterialList != null) - { - materials = brushMaterialList.materialAssets.ToDictionary(x => x.name, y => y.asset); - Console.Print("materials dictionary with " + materials.Count + " entries"); - } - else - { - materials = new Dictionary(); - Console.Print("no materials dictionary found"); - } - } - - if (!materials.TryGetValue(textureName, out brushMaterial)) - { - Console.Print("Material '" + textureName + "' not found for brush"); - materials.Add(textureName, material); - } - - for (int i = 0; i < brushVertices.Length; i += 3) - { - Vector3 v1 = brushVertices[i + 0]; - Vector3 v2 = brushVertices[i + 1]; - Vector3 v3 = brushVertices[i + 2]; - - Vector3 normal = -Vector3.Cross(v3 - v1, v2 - v1).Normalized; - - // fetch the texture parameters from the plane with matching normal - Vector2 uvScale = new Vector2(0f); - float uvRotation = 0f; - Vector2 uvOffset = new Vector2(0f); - bool found = false; - foreach (var brushPlane in brush.planes) - { - if ((brushPlane.plane.Normal - normal).Length < 0.01f) - { - normal = brushPlane.plane.Normal; // for consistency - uvScale = 1f / brushPlane.scale; - uvRotation = brushPlane.rotation; - uvOffset = brushPlane.offset * brushPlane.scale; - found = true; - break; - } - } - - if (!found) - Console.Print("no matching plane found, bad geometry?"); - - Vector2 uv1, uv2, uv3; - // if quake format - { - // The texture is projected to the surface from three angles, the axis with least - // distortion is chosen here. - - // Attempt to workaround most rounding errors at 45-degree angles which causes bias towards one axis. - // This behaviour is seemingly random in different engines and editors, so let's not bother. - Vector3 textureNormal = new Vector3((float)Math.Round(normal.X, 4), - (float)Math.Round(normal.Y, 4), (float)Math.Round(normal.Z, 4)); - - var dotX = Math.Abs(Vector3.Dot(textureNormal, Vector3.Right)); - var dotY = Math.Abs(Vector3.Dot(textureNormal, Vector3.Up)); - var dotZ = Math.Abs(Vector3.Dot(textureNormal, Vector3.Forward)); - - Vector3 axis; - if (dotY >= dotX && dotY >= dotZ) - axis = -Vector3.Up; - else if (dotX >= dotY && dotX >= dotZ) - axis = Vector3.Right; - else if (dotZ >= dotX && dotZ >= dotY) - axis = -Vector3.Forward; - else - axis = Vector3.Right; - - var axisForward = Mathf.Abs(Vector3.Dot(axis, Vector3.Up)) > 0.01f - ? -Vector3.Forward - : Vector3.Up; - var axisForward2 = Mathf.Abs(Vector3.Dot(axis, Vector3.Up)) > 0.01f - ? Vector3.Up - : -Vector3.Forward; - - Quaternion rot = Quaternion.Identity; - rot = rot * Quaternion.LookRotation(axis, axisForward); - rot = rot * Quaternion.RotationAxis(-Vector3.Forward, - 180f * Mathf.DegreesToRadians); - rot = rot * Quaternion.RotationAxis( - Mathf.Abs(Vector3.Dot(axis, Vector3.Right)) > 0.01f - ? Vector3.Right - : axisForward2, - uvRotation * Mathf.DegreesToRadians); - - uv1 = ((Vector2)(v1 * rot) + uvOffset) * uvScale; - uv2 = ((Vector2)(v2 * rot) + uvOffset) * uvScale; - uv3 = ((Vector2)(v3 * rot) + uvOffset) * uvScale; - } - - brushUvs[i + 0] = uv1; - brushUvs[i + 1] = uv2; - brushUvs[i + 2] = uv3; - - brushNormals[i + 0] = normal; - brushNormals[i + 1] = normal; - brushNormals[i + 2] = normal; - } - - vertices.AddRange(brushVertices); - uvs.AddRange(brushUvs); - normals.AddRange(brushNormals); - - if (vertices.Count > 0) - { - uint[] triangles = new uint[vertices.Count]; - for (uint i = 0; i < vertices.Count; i++) - triangles[i] = i; - - Model model = Content.CreateVirtualAsset(); - model.SetupLODs(new int[] { 1 }); - model.LODs[0].Meshes[0].UpdateMesh(vertices.ToArray(), (int[])(object)triangles, normals.ToArray(), - null, uvs.ToArray()); - - StaticModel childModel = Actor.AddChild(); - childModel.Name = "Brush_" + brushIndex; - childModel.Model = model; - childModel.SetMaterial(0, brushMaterial); - childModel.Parent = mapRootActor; - - CollisionData collisionData = Content.CreateVirtualAsset(); - if (collisionData.CookCollision(convexMesh ? CollisionDataType.ConvexMesh : CollisionDataType.TriangleMesh, vertices.ToArray(), - triangles.ToArray())) - { - bool failed = true; - if (convexMesh) - { - // fallback to triangle mesh - failed = collisionData.CookCollision(CollisionDataType.TriangleMesh, - vertices.ToArray(), - triangles.ToArray()); - if (!failed) - Console.PrintWarning("Hull brush " + brushIndex.ToString() + " is not convex"); - } - if (failed) - throw new Exception("failed to cook final collision"); - } - - var meshCollider = childModel.AddChild(); - meshCollider.CollisionData = collisionData; - } + brushGeometries.Add(geom); } catch (Exception e) { - Console.Print("Failed to hull brush " + brushIndex.ToString() + ": " + e.Message); + Console.Print("Failed to triangulate brush " + brushIndex.ToString() + ": " + e.Message); } - brushIndex++; } + sw.Stop(); + Console.Print("Pass 1: triangulation: " + sw.Elapsed.TotalMilliseconds + "ms"); + + // pass 2: texturing + sw.Restart(); + foreach (var geom in brushGeometries) + { + var brushVertices = geom.vertices; + Vector2[] brushUvs = new Vector2[brushVertices.Length]; + Vector3[] brushNormals = new Vector3[brushVertices.Length]; + + var textureName = geom.brush.planes[0].texture; + + if (materials == null) + { + var customSettings = Engine.GetCustomSettings("BrushMaterials"); + BrushMaterialList brushMaterialList = customSettings?.CreateInstance(); + if (brushMaterialList != null) + { + materials = brushMaterialList.materialAssets.ToDictionary(x => x.name, y => y.asset); + Console.Print("materials dictionary with " + materials.Count + " entries"); + } + else + { + materials = new Dictionary(); + Console.Print("no materials dictionary found"); + } + } + + // FIXME: brush can have multiple textures + if (!materials.TryGetValue(textureName, out geom.brushMaterial)) + { + Console.Print("Material '" + textureName + "' not found for brush"); + materials.Add(textureName, material); + } + + for (int i = 0; i < brushVertices.Length; i += 3) + { + Vector3 v1 = brushVertices[i + 0]; + Vector3 v2 = brushVertices[i + 1]; + Vector3 v3 = brushVertices[i + 2]; + + Vector3 normal = -Vector3.Cross(v3 - v1, v2 - v1).Normalized; + + // fetch the texture parameters from the plane with matching normal + Vector2 uvScale = new Vector2(0f); + float uvRotation = 0f; + Vector2 uvOffset = new Vector2(0f); + bool found = false; + foreach (var brushPlane in geom.brush.planes) + { + if ((brushPlane.plane.Normal - normal).Length < 0.01f) + { + normal = brushPlane.plane.Normal; // for consistency + uvScale = 1f / brushPlane.scale; + uvRotation = brushPlane.rotation; + uvOffset = brushPlane.offset * brushPlane.scale; + found = true; + break; + } + } + + if (!found) + Console.Print("no matching plane found, bad geometry?"); + + Vector2 uv1, uv2, uv3; + // if quake format + { + // The texture is projected to the surface from three angles, the axis with least + // distortion is chosen here. + + // Attempt to workaround most rounding errors at 45-degree angles which causes bias towards one axis. + // This behaviour is seemingly random in different engines and editors, so let's not bother. + + Vector3 textureNormal = new Vector3((float)Math.Round(normal.X, 4), + (float)Math.Round(normal.Y, 4), (float)Math.Round(normal.Z, 4)); + + var dotX = Math.Abs(Vector3.Dot(textureNormal, Vector3.Right)); + var dotY = Math.Abs(Vector3.Dot(textureNormal, Vector3.Up)); + var dotZ = Math.Abs(Vector3.Dot(textureNormal, Vector3.Forward)); + + Vector3 axis; + if (dotY >= dotX && dotY >= dotZ) + axis = -Vector3.Up; + else if (dotX >= dotY && dotX >= dotZ) + axis = Vector3.Right; + else if (dotZ >= dotX && dotZ >= dotY) + axis = -Vector3.Forward; + else + axis = Vector3.Right; + + var axisForward = Mathf.Abs(Vector3.Dot(axis, Vector3.Up)) > 0.01f + ? -Vector3.Forward + : Vector3.Up; + var axisForward2 = Mathf.Abs(Vector3.Dot(axis, Vector3.Up)) > 0.01f + ? Vector3.Up + : -Vector3.Forward; + + Quaternion rot = Quaternion.Identity; + rot = rot * Quaternion.LookRotation(axis, axisForward); + rot = rot * Quaternion.RotationAxis(-Vector3.Forward, + 180f * Mathf.DegreesToRadians); + rot = rot * Quaternion.RotationAxis( + Mathf.Abs(Vector3.Dot(axis, Vector3.Right)) > 0.01f + ? Vector3.Right + : axisForward2, + uvRotation * Mathf.DegreesToRadians); + + uv1 = ((Vector2)(v1 * rot) + uvOffset) * uvScale; + uv2 = ((Vector2)(v2 * rot) + uvOffset) * uvScale; + uv3 = ((Vector2)(v3 * rot) + uvOffset) * uvScale; + } + + brushUvs[i + 0] = uv1; + brushUvs[i + 1] = uv2; + brushUvs[i + 2] = uv3; + + brushNormals[i + 0] = normal; + brushNormals[i + 1] = normal; + brushNormals[i + 2] = normal; + } + + geom.vertices = brushVertices; + geom.uvs = brushUvs; + geom.normals = brushNormals; + geom.indices = new uint[geom.vertices.Length]; + for (uint i = 0; i < geom.vertices.Length; i++) + geom.indices[i] = i; + + if (geom.vertices.Length > 0) + { + geom.model = Content.CreateVirtualAsset(); + geom.model.SetupLODs(new int[] { 1 }); + geom.model.LODs[0].Meshes[0].UpdateMesh(geom.vertices, geom.indices, geom.normals, + null, geom.uvs); + + /* + StaticModel childModel = Actor.AddChild(); + childModel.Name = "Brush_" + brushIndex; + childModel.Model = geom.model; + childModel.SetMaterial(0, geom.brushMaterial); + childModel.Parent = mapRootActor; + + CollisionData collisionData = Content.CreateVirtualAsset(); + if (collisionData.CookCollision(convexMesh ? CollisionDataType.ConvexMesh : CollisionDataType.TriangleMesh, geom.vertices.ToArray(), + geom.indices.ToArray())) + { + bool failed = true; + if (convexMesh) + { + // fallback to triangle mesh + failed = collisionData.CookCollision(CollisionDataType.TriangleMesh, + geom.vertices.ToArray(), + geom.indices.ToArray()); + if (!failed) + Console.PrintWarning("Hull brush " + brushIndex.ToString() + " is not convex"); + } + if (failed) + throw new Exception("failed to cook final collision"); + } + + var meshCollider = childModel.AddChild(); + meshCollider.CollisionData = collisionData; + */ + } + + //brushGeometries.Add(geom); + + + } + sw.Stop(); + Console.Print("Pass 2: texturing: " + sw.Elapsed.TotalMilliseconds + "ms"); + + // pass 3: collision + sw.Restart(); + foreach (var geom in brushGeometries) + { + StaticModel childModel = Actor.AddChild(); + childModel.Name = "Brush_" + brushIndex; + childModel.Model = geom.model; + childModel.SetMaterial(0, geom.brushMaterial); + childModel.Parent = mapRootActor; + + CollisionData collisionData = Content.CreateVirtualAsset(); + if (collisionData.CookCollision(convexMesh ? CollisionDataType.ConvexMesh : CollisionDataType.TriangleMesh, geom.vertices, + geom.indices)) + { + bool failed = true; + if (convexMesh) + { + // fallback to triangle mesh + failed = collisionData.CookCollision(CollisionDataType.TriangleMesh, + geom.vertices, + geom.indices); + if (!failed) + Console.PrintWarning("Hull brush " + brushIndex.ToString() + " is not convex"); + } + if (failed) + throw new Exception("failed to cook final collision"); + } + + var meshCollider = childModel.AddChild(); + meshCollider.CollisionData = collisionData; + } + sw.Stop(); + Console.Print("Pass 3: collision: " + sw.Elapsed.TotalMilliseconds + "ms"); } else { diff --git a/Source/GoakeTestsTarget.Build.cs b/Source/GoakeTestsTarget.Build.cs new file mode 100644 index 0000000..98be7d4 --- /dev/null +++ b/Source/GoakeTestsTarget.Build.cs @@ -0,0 +1,32 @@ +using System; +using Flax.Build; + +public class GoakeTestsTarget : Target +{ + /// + public GoakeTestsTarget() + { + Name = ProjectName = OutputName = "GoakeTests"; + } + + /// + public override void Init() + { + base.Init(); + + IsPreBuilt = false; + Type = TargetType.DotNet; + OutputType = TargetOutputType.Library; + /*Platforms = new[] + { + Flax.Build.Platform.BuildPlatform.Target, + }; + Configurations = new[] + { + TargetConfiguration.Debug, + TargetConfiguration.Release, + };*/ + + CustomExternalProjectFilePath = System.IO.Path.Combine("Tests/GoakeTests.csproj"); + } +} diff --git a/Tests/Cabrito/ConsoleTests.cs b/Tests/Cabrito/ConsoleTests.cs new file mode 100644 index 0000000..fc41c54 --- /dev/null +++ b/Tests/Cabrito/ConsoleTests.cs @@ -0,0 +1,43 @@ +using NUnit.Framework; +using Cabrito; +using Console = Cabrito.Console; + +namespace GoakeTests.ConsoleTests +{ + public class ConsoleTests + { + [SetUp] + public void Setup() + { + Console.Init(); + } + + [TearDown] + public void CleanUp() + { + Console.Destroy(); + } + + [Test] + public void Test1() + { + string testPrint = "Hello world!"; + + Console.OnPrint += delegate(string s) { Assert.True(s == testPrint); }; + + Console.Print(testPrint); + } + + [Test] + public void Test2() + { + string testPrint = "Hello world2!"; + + Console.OnPrint += delegate(string s) { Assert.True(s == testPrint); }; + + Console.Print(testPrint); + } + + + } +} \ No newline at end of file diff --git a/Tests/GoakeTests.csproj b/Tests/GoakeTests.csproj new file mode 100644 index 0000000..e2a742e --- /dev/null +++ b/Tests/GoakeTests.csproj @@ -0,0 +1,20 @@ + + + + + false + + net452;net462;net472;net48;net5.0;net5.0-windows;net6.0 + + + + + + + + + + + + + diff --git a/Tests/MapParser/MapParserTests.cs b/Tests/MapParser/MapParserTests.cs new file mode 100644 index 0000000..e917253 --- /dev/null +++ b/Tests/MapParser/MapParserTests.cs @@ -0,0 +1,74 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using NUnit.Framework; +using Cabrito; +using FlaxEngine; +using Game; + +namespace GoakeTests.MapParser +{ + public class MapParserTests + { + private byte[] aerowalkBytes; + private MapEntity aerowalkRoot; + + [SetUp] + public void Setup() + { + aerowalkBytes = File.ReadAllBytes(@"C:\dev\Goake\maps\aerowalk\aerowalk.map"); + aerowalkRoot = Game.MapParser.Parse(aerowalkBytes); + } + + [TearDown] + public void CleanUp() + { + + } + + [Test] + public void Perf_LoadAerowalk() + { + List mapEntities = new List(100); + + Stopwatch sw = Stopwatch.StartNew(); + + for (int i = 0; i < 100; i++) + { + var root = Game.MapParser.Parse(aerowalkBytes); + mapEntities.Add(root); + } + + sw.Stop(); + + var elapsedMs = sw.Elapsed.TotalMilliseconds; + TestContext.Out.WriteLine("Map parsing time: " + elapsedMs/100 + "ms"); + } + + [Test] + public void Perf_TriangulateAerowalk() + { + // warmup? + //var roott = Game.MapParser.Parse(aerowalkBytes); + + List mapEntities = new List(100); + + Stopwatch sw = Stopwatch.StartNew(); + + for (int i = 0; i < 1; i++) + { + foreach (var ent in aerowalkRoot.entities) + foreach (var brush in ent.brushes) + { + Q3MapImporter.TriangulateBrush3(brush, out Vector3[] verts); + Assert.IsTrue(verts.Length > 0); + } + } + + sw.Stop(); + + var elapsedMs = sw.Elapsed.TotalMilliseconds; + TestContext.Out.WriteLine("Triangulation time: " + elapsedMs + "ms"); + } + } +} \ No newline at end of file diff --git a/lore.md b/lore.md new file mode 100644 index 0000000..8880cf4 --- /dev/null +++ b/lore.md @@ -0,0 +1,19 @@ +- overall tone should be fantasy-like (medieval) with hints of future/alien technology +- (year 4xxx in the future,) earth is dead, humankind is mostly dead, only ruins are left of the civilization +- previously technologically advanced civilization died thousands years ago +- +- goat-like humanoid creatures are waking up from deep sleep around the earth + +- levels a mix of future and historical architecture (imagine ancients depicted artifacts but clearly technological in nature) + - old stone ruins mixed with a sparkle of futuristic tech + - crashed space ships with mostly futuristic texturing and materials + - + +- weapons + - rocket launcher: technologically infused glove with engraved rune writing on it, basically a fireball thrower. possibly something everyone is carrying already but lacks power/ammo which can be found in the levels. + - nailgun: a homage to quake's super nailgun, looks like it was built in current day (early 2000) technology. has a heating element which can provide more punch to the nails making them more lethal (basic nailgun = starter weapon, ammo promotes it to super nailgun). + - crossbow: ancient looking crossbow capable of firing huge bolts that can be used to gain leverage around the levels, has a slot for a power crystal (battery) but is missing one or has a depleted one. adding power to this would increase the damage by tenfold, making it capable of instagibbing targets. + - shotgun: very powerful and inaccurate shotgun which shoots shrapnel that can ricochet from walls and still hit the target. possibly homage to flak cannon in unreal series. packs enough punch to even push the player backwards while in mid-air. + - lightning gun: ? + - melee weapon: can be almost anything from swords to hammers to laser sword and tasers. no pummel. can push back the player a bit when hitting floor or walls. + - \ No newline at end of file