Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate import into steps and other changes #27

Open
wants to merge 12 commits into
base: develop
Choose a base branch
from
147 changes: 147 additions & 0 deletions Editor/ADTUtility.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
using Newtonsoft.Json;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;

namespace WowUnity
{
public class ADTUtility
{
public static bool IsAdtObj(string path)
{
return Regex.IsMatch(Path.GetFileName(path), @"^adt_\d+_\d+(_.+)?\.obj$");
}

public static bool IsAdtAny(string path)
{
return Regex.IsMatch(Path.GetFileName(path), @"^adt_\d+_\d+(_.+)?\.(prefab|obj)$");
}

public static void PostProcessImports(List<string> paths)
{
var total = 0f;
var itemsProcessed = 0f;

List<(string, GameObject)> instances = new();
AssetDatabase.StartAssetEditing();
try
{
foreach (var path in paths)
{
if (M2Utility.FindPrefab(path) != null)
{
continue;
}

var imported = AssetDatabase.LoadAssetAtPath<GameObject>(path);
Renderer[] renderers = imported.GetComponentsInChildren<Renderer>();
total += renderers.Count() + 1;
}

var mainDataPath = Application.dataPath.Replace("Assets", "");
foreach (var path in paths)
{
if (M2Utility.FindPrefab(path) != null)
{
continue;
}

var importedInstance = M2Utility.InstantiateImported(path);

var dirName = Path.GetDirectoryName(path);

Renderer[] renderers = importedInstance.GetComponentsInChildren<Renderer>();

foreach (var renderer in renderers)
{
var pathToMetadata = $"{dirName}/tex_{renderer.name}.json";

if (EditorUtility.DisplayCancelableProgressBar("Creating terrain materials.", pathToMetadata, itemsProcessed / total))
{
return;
}

var sr = new StreamReader(mainDataPath + pathToMetadata);
var jsonData = sr.ReadToEnd();
sr.Close();

var metadata = JsonConvert.DeserializeObject<Tex>(jsonData);
if (metadata.layers.Count == 0)
{
continue;
}

for (var idx = 0; idx < metadata.layers.Count; idx++)
{
var texture = metadata.layers[idx];
texture.assetPath = Path.GetRelativePath(mainDataPath, Path.GetFullPath(Path.Join(dirName, texture.file)));
metadata.layers[idx] = texture;
}

renderer.material = MaterialUtility.GetTerrainMaterial(dirName, renderer.name, metadata);
itemsProcessed++;
}

instances.Add((path, importedInstance));
}
}
catch (System.Exception)
{
Debug.LogError($"failed processing terrain materials");
throw;
}
finally
{
AssetDatabase.StopAssetEditing();
AssetDatabase.SaveAssets();
EditorUtility.ClearProgressBar();
}

try
{
foreach (var (path, instance) in instances)
{
M2Utility.SaveAsPrefab(instance, path);
itemsProcessed++;
}
} finally
{
EditorUtility.ClearProgressBar();
}
}

public class Tex
{
public List<Layer> layers;
}

public class Layer
{
public uint index;
public uint effectID;
public float scale;
public uint fileDataID;
public string file;
public string assetPath;
}

public class EffectModel
{
public int fileDataID;
public string fileName;
}

public class Effect
{
public int ID;
public int Density;
public int Sound;
public List<int> DoodadID;
public List<float> DoodadWeight;
public Dictionary<string, EffectModel> DoodadModelIDs;
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

216 changes: 210 additions & 6 deletions Editor/AssetConversionManager.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,222 @@
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;

namespace WowUnity
{
class AssetConversionManager
public class AssetConversionManager
{
public static void ProcessAssets()
private static readonly ConcurrentQueue<string> importedModelPathQueue = new();
private static readonly ConcurrentQueue<string> importedWMOPathQueue = new();
private static readonly ConcurrentQueue<string> physicsQueue = new();
private static bool isBusy = false;

public static void QueuePostprocess(string filePath)
{
importedModelPathQueue.Enqueue(filePath);
}

public static bool HasQueue()
{
return importedModelPathQueue.Count + importedWMOPathQueue.Count + physicsQueue.Count > 0;
}

public static bool IsBusy()
{
return isBusy;
}

public static string ReadAssetJson(string path)
{
var dirName = Path.GetDirectoryName(path);
string pathToMetadata = dirName + "/" + Path.GetFileNameWithoutExtension(path) + ".json";
string mainDataPath = Application.dataPath.Replace("Assets", "");

var sr = new StreamReader(mainDataPath + pathToMetadata);
var jsonData = sr.ReadToEnd();
sr.Close();

return jsonData;
}

public static void RunPostProcessImports()
{
var itemsToProcess = importedModelPathQueue.Count;
var itemsProcessed = 0f;

List<(string, TextAsset)> hasPlacement = new();

AssetDatabase.StartAssetEditing();
try
{
while (importedModelPathQueue.TryDequeue(out string path))
{
Debug.Log($"{path}: postprocessing");

var jsonData = ReadAssetJson(path);

if (WMOUtility.IsWMO(jsonData))
{
// process this separately because of StartAssetEditing issues
importedWMOPathQueue.Enqueue(path);
continue;
}

if (EditorUtility.DisplayCancelableProgressBar("Postprocessing WoW assets", path, itemsProcessed / itemsToProcess))
{
return;
}

M2Utility.PostProcessImport(path, jsonData);

// process this separately because of StartAssetEditing issues
physicsQueue.Enqueue(path);

itemsProcessed++;
}
}
finally
{
AssetDatabase.StopAssetEditing();
AssetDatabase.SaveAssets();
}

while (importedWMOPathQueue.TryDequeue(out string path))
{
Debug.Log($"{path}: postprocessing");

if (EditorUtility.DisplayCancelableProgressBar("Postprocessing WoW WMOs", path, itemsProcessed / itemsToProcess))
{
return;
}

WMOUtility.PostProcessImport(path, ReadAssetJson(path));
SetupPhysics(path);

TextAsset placementData = AssetDatabase.LoadAssetAtPath<TextAsset>(path.Replace(".obj", "_ModelPlacementInformation.csv"));
if (placementData != null)
{
hasPlacement.Add((path, placementData));
}

itemsProcessed++;
}

itemsToProcess = physicsQueue.Count;
itemsProcessed = 0f;

while (physicsQueue.TryDequeue(out string path))
{
Debug.Log($"{path}: setup physics");

if (EditorUtility.DisplayCancelableProgressBar("Setting up physics", path, itemsProcessed / itemsToProcess))
{
return;
}

SetupPhysics(path);

itemsProcessed++;
}

itemsToProcess = hasPlacement.Count;
itemsProcessed = 0f;

foreach (var (path, placementData) in hasPlacement)
{
if (EditorUtility.DisplayCancelableProgressBar("Placing doodads", path, itemsProcessed / itemsToProcess))
{
return;
}
Debug.Log($"{path}: placing models");
ItemCollectionUtility.PlaceModels(M2Utility.FindPrefab(path), placementData);
itemsProcessed++;
}
}

public static void SetupPhysics(string path)
{
GameObject physicsPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(path.Replace(".obj", ".phys.obj"));
if (physicsPrefab == null)
{
return;
}

var prefab = M2Utility.FindPrefab(path);

if (prefab.transform.Find("Collision") != null)
{
return;
}

var collisionMesh = physicsPrefab.GetComponentInChildren<MeshFilter>();
if (collisionMesh == null)
{
return;
}

var prefabInst = PrefabUtility.InstantiatePrefab(prefab) as GameObject;
prefabInst.GetComponentsInChildren<MeshCollider>().ToList().ForEach(collider => Object.DestroyImmediate(collider));

GameObject collider = new();
collider.transform.SetParent(prefabInst.transform);
collider.name = "Collision";
MeshCollider parentCollider = collider.AddComponent<MeshCollider>();
parentCollider.sharedMesh = collisionMesh.sharedMesh;
PrefabUtility.ApplyPrefabInstance(prefabInst, InteractionMode.AutomatedAction);
PrefabUtility.SavePrefabAsset(prefab);

Object.DestroyImmediate(prefabInst);
}

public static void PostProcessImports()
{
if (importedModelPathQueue.Count + importedWMOPathQueue.Count + physicsQueue.Count == 0)
{
return;
}

isBusy = true;

try
{
RunPostProcessImports();
}
finally
{
EditorUtility.ClearProgressBar();
}

Debug.Log("PostProcessImports done");
isBusy = false;
}

public static void JobPostprocessAllAssets()
{
EditorApplication.update -= ProcessAssets;
importedModelPathQueue.Clear();
importedWMOPathQueue.Clear();
physicsQueue.Clear();

M2Utility.PostProcessImports();
ItemCollectionUtility.BeginQueue();
EditorUtility.DisplayProgressBar("Postprocessing WoW assets", "Looking for assets.", 0);
try
{
string[] allAssets = AssetDatabase.GetAllAssetPaths();
foreach (string path in allAssets)
{
if (WoWExportUnityPostprocessor.ValidAsset(path) && !ADTUtility.IsAdtObj(path))
{
QueuePostprocess(path);
}
}
}
finally
{
EditorUtility.ClearProgressBar();
}
PostProcessImports();
}
}
}
Loading