brush texture to material mapping and material changes
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user