brush texture to material mapping and material changes

This commit is contained in:
GoaLitiuM
2021-08-22 22:21:55 +03:00
parent 7466590d4f
commit 995e9bdbce
55 changed files with 777 additions and 338 deletions

View File

@@ -81,7 +81,7 @@ namespace Game
/// exceptionally close to each other, this value might need to be
/// adjusted.
/// </summary>
const float EPSILON = 0.0001f;
const float EPSILON = 0.001f;
/// <summary>
/// Struct representing a single face.
@@ -1086,7 +1086,10 @@ namespace Game
public class Q3MapImporter : Script
{
private string mapPath = @"C:\dev\GoakeFlax\Assets\Maps\cube.map";
//private string mapPath = @"C:\dev\GoakeFlax\Assets\Maps\cube_q1.map";
private string mapPath = @"C:\dev\GoakeFlax\Assets\Maps\cube_q3.map";
//private string mapPath = @"C:\dev\GoakeFlax\Assets\Maps\cube_valve.map";
//private string mapPath = @"C:\dev\Goake\maps\aerowalk\aerowalk.map";
//private string mapPath = @"C:\dev\GoakeFlax\Assets\Maps\problematic.map";
@@ -1672,27 +1675,59 @@ namespace Game
byte[] mapChars = File.ReadAllBytes(mapPath);
root = MapParser.Parse(mapChars);
List<Vector3> vertices = new List<Vector3>();
List<Vector2> uvs = new List<Vector2>();
List<Vector3> normals = new List<Vector3>();
if (true)
bool oneMesh = false;
bool convexMesh = true;
if (!oneMesh)
{
Dictionary<string, MaterialBase> materials = null;
var mapRootActor = Actor.AddChild<Actor>();
mapRootActor.Name = "MapRootActor";
int brushIndex = 0;
foreach (var brush in root.entities[0].brushes)
{
try
{
Vector3[] brushVertices;
TriangulateBrush3(brush, out brushVertices);
List<Vector3> vertices = new List<Vector3>();
List<Vector2> uvs = new List<Vector2>();
List<Vector3> normals = new List<Vector3>();
TriangulateBrush3(brush, out Vector3[] brushVertices);
Vector2[] brushUvs = new Vector2[brushVertices.Length];
Vector3[] brushNormals = new Vector3[brushVertices.Length];
for (int i=0; i<brushVertices.Length; i+=3)
// FIXME: brush can have multiple textures
MaterialBase brushMaterial;
var textureName = brush.planes[0].texture;
if (materials == null)
{
Vector3 v1 = brushVertices[i+0];
Vector3 v2 = brushVertices[i+1];
Vector3 v3 = brushVertices[i+2];
var customSettings = Engine.GetCustomSettings("BrushMaterials");
BrushMaterialList brushMaterialList = customSettings?.CreateInstance<BrushMaterialList>();
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<string, MaterialBase>();
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;
@@ -1701,19 +1736,19 @@ namespace Game
float uvRotation = 0f;
Vector2 uvOffset = new Vector2(0f);
bool found = false;
Vector2 textureSize = new Vector2(64f); // TODO: figure out the correct size for the material
foreach (var brushPlane in brush.planes)
{
if ((brushPlane.plane.Normal - normal).Length < 0.01f)
{
normal = brushPlane.plane.Normal; // for consistency
uvScale = 1f / brushPlane.scale / textureSize; // texture size?
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?");
@@ -1751,9 +1786,163 @@ namespace Game
Quaternion rot = Quaternion.Identity;
rot = rot * Quaternion.LookRotation(axis, axisForward);
rot = rot * Quaternion.RotationAxis(-Vector3.Forward, 180f * Mathf.DegreesToRadians);
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,
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>();
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<StaticModel>();
childModel.Name = "Brush_" + brushIndex;
childModel.Model = model;
childModel.SetMaterial(0, brushMaterial);
childModel.Parent = mapRootActor;
CollisionData collisionData = Content.CreateVirtualAsset<CollisionData>();
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>();
meshCollider.CollisionData = collisionData;
}
}
catch (Exception e)
{
Console.Print("Failed to hull brush " + brushIndex.ToString() + ": " + e.Message);
}
brushIndex++;
}
}
else
{
List<Vector3> vertices = new List<Vector3>();
List<Vector2> uvs = new List<Vector2>();
List<Vector3> normals = new List<Vector3>();
int brushIndex = 0;
foreach (var brush in root.entities[0].brushes)
{
try
{
TriangulateBrush3(brush, out Vector3[] brushVertices);
Vector2[] brushUvs = new Vector2[brushVertices.Length];
Vector3[] brushNormals = new Vector3[brushVertices.Length];
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;
@@ -1778,34 +1967,33 @@ namespace Game
{
Console.Print("Failed to hull brush " + brushIndex.ToString() + ": " + e.Message);
}
brushIndex++;
}
}
if (vertices.Count > 0)
{
uint[] triangles = new uint[vertices.Count];
for (uint i = 0; i < vertices.Count; i++)
triangles[i] = i;
if (vertices.Count > 0)
{
uint[] triangles = new uint[vertices.Count];
for (uint i = 0; i < vertices.Count; i++)
triangles[i] = i;
model = Content.CreateVirtualAsset<Model>();
model.SetupLODs(new int[] { 1 });
model.LODs[0].Meshes[0].UpdateMesh(vertices.ToArray(), (int[])(object)triangles, normals.ToArray(), null, uvs.ToArray());
model = Content.CreateVirtualAsset<Model>();
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<StaticModel>();
childModel.Name = "MapModel";
childModel.Model = model;
childModel.SetMaterial(0, material);
StaticModel childModel = Actor.AddChild<StaticModel>();
childModel.Name = "MapModel";
childModel.Model = model;
childModel.SetMaterial(0, material);
CollisionData collisionData = Content.CreateVirtualAsset<CollisionData>();
if (collisionData.CookCollision(CollisionDataType.TriangleMesh, vertices.ToArray(), triangles.ToArray()))
throw new Exception("failed to cook final collision");
var meshCollider = childModel.AddChild<MeshCollider>();
meshCollider.CollisionData = collisionData;
// TODO: flip Y and Z
//childModel.Orientation = Quaternion.RotationYawPitchRoll(180f*Mathf.DegreesToRadians, -90f*Mathf.DegreesToRadians, 0f);
//childModel.Scale = new Vector3(1f, -1f, 1f);
CollisionData collisionData = Content.CreateVirtualAsset<CollisionData>();
if (collisionData.CookCollision(CollisionDataType.TriangleMesh, vertices.ToArray(),
triangles.ToArray()))
throw new Exception("failed to cook final collision");
var meshCollider = childModel.AddChild<MeshCollider>();
meshCollider.CollisionData = collisionData;
}
}
}