Skip to content

Instantly share code, notes, and snippets.

@gatosyocora
Created October 11, 2019 12:28
Show Gist options
  • Save gatosyocora/4886ca95364bd4f8d2c58c434a5bec07 to your computer and use it in GitHub Desktop.
Save gatosyocora/4886ca95364bd4f8d2c58c434a5bec07 to your computer and use it in GitHub Desktop.
Unityアプリで任意の場所にあるmatファイルをランタイムロードするプログラム(暫定なのでエラー処理とか書いてないです)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Windows.Forms;
using System.IO;
using YamlDotNet;
using YamlDotNet.RepresentationModel;
using System.Text.RegularExpressions;
// Copyright (c) 2019 gatosyocora
// Released under the MIT license
// https://opensource.org/licenses/MIT
// Use YamlDotNet for Unity
public class RuntimeMaterialLoader : MonoBehaviour {
private Dictionary<string, string> guid2PathDict;
/// <summary>
/// マテリアルファイルを読み込む
/// </summary>
public void LoadMaterialFile()
{
string filePath = ChooseFile();
// シェーダーファイルのランタイムロードはつらいのでとりあえずUnlit/Texture
Material mat = new Material(Shader.Find("Unlit/Texture"));
/// guidリストをつくるための開始フォルダを決めるために取得
var rootFolderPath = GetRootFolderPath(filePath, "Assets");
guid2PathDict = GetPairGuidAndFilePath(rootFolderPath);
var yaml = new YamlStream();
using (var file = new StreamReader(filePath, System.Text.Encoding.UTF8))
{
yaml.Load(file);
}
var mapping = (YamlMappingNode)yaml.Documents[0].RootNode;
var materialInfo = (YamlMappingNode)mapping.Children["Material"];
var properties = (YamlMappingNode)materialInfo["m_SavedProperties"];
mat.name = ((YamlScalarNode)materialInfo["m_Name"]).Value;
// テクスチャ情報
var textures = (YamlSequenceNode)properties["m_TexEnvs"];
foreach (YamlMappingNode texture in textures)
{
foreach (var child in texture)
{
var key = ((YamlScalarNode)child.Key).Value;
var values = (YamlMappingNode)child.Value;
var tex = (YamlMappingNode)values["m_Texture"];
var scale = (YamlMappingNode)values["m_Scale"];
var offset = (YamlMappingNode)values["m_Offset"];
var texID = ((YamlScalarNode)tex["fileID"]).Value;
// guidのキーが含まれているかどうか
foreach (var k in tex)
{
if (((YamlScalarNode)k.Key).Value == "guid")
{
string texGuid = ((YamlScalarNode)tex["guid"]).Value;
Texture2D texture2d = LoadTextureWithGuid(texGuid);
mat.SetTexture(key, texture2d);
mat.SetTextureScale(
key,
new Vector2(
float.Parse(((YamlScalarNode)scale["x"]).Value),
float.Parse(((YamlScalarNode)scale["y"]).Value)
)
);
mat.SetTextureOffset(
key,
new Vector2(
float.Parse(((YamlScalarNode)offset["x"]).Value),
float.Parse(((YamlScalarNode)offset["y"]).Value)
)
);
}
}
// Debug.LogFormat("{0}, {1}", key, texID);
}
}
// float情報
var floats = (YamlSequenceNode)properties["m_Floats"];
foreach (YamlMappingNode f in floats)
{
foreach (var child in f)
{
var key = ((YamlScalarNode)child.Key).Value;
var value = ((YamlScalarNode)child.Value).Value;
// Debug.LogFormat("{0}, {1}", key, value);
mat.SetFloat(key, float.Parse(value));
}
}
// 色情報
var colors = (YamlSequenceNode)properties["m_Colors"];
foreach (YamlMappingNode color in colors)
{
foreach (var child in color)
{
var key = ((YamlScalarNode)child.Key).Value;
var value = (YamlMappingNode)child.Value;
var r = float.Parse(((YamlScalarNode)value["r"]).Value);
var g = float.Parse(((YamlScalarNode)value["g"]).Value);
var b = float.Parse(((YamlScalarNode)value["b"]).Value);
var a = float.Parse(((YamlScalarNode)value["a"]).Value);
// Debug.LogFormat("{0}, ({1}, {2}, {3}, {4})", key, r, g, b, a);
mat.SetColor(key, new Color(r, g, b, a));
}
}
var renderer = this.gameObject.GetComponent<MeshRenderer>();
renderer.sharedMaterial = mat;
}
/// <summary>
/// ダイアログを表示してファイルを選択する
/// </summary>
/// <returns></returns>
private string ChooseFile()
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.FileName = "default.html";
ofd.InitialDirectory = @"C:\";
ofd.Title = "開くファイルを選択してください";
ofd.RestoreDirectory = true;
//ダイアログを表示する
if (ofd.ShowDialog() == DialogResult.OK)
{
return ofd.FileName;
}
else
{
return "";
}
}
/// <summary>
/// guidを使ってテクスチャを読み込む
/// </summary>
/// <param name="guid"></param>
/// <returns></returns>
private Texture2D LoadTextureWithGuid(string guid)
{
Texture2D texture = null;
string path = GetFilePathFromGuid(guid);
if (File.Exists(path))
{
var fs = new FileStream(path, FileMode.Open, FileAccess.Read);
var reader = new BinaryReader(fs);
var binary = reader.ReadBytes((int)reader.BaseStream.Length);
reader.Close();
fs.Dispose();
fs = null;
texture = new Texture2D(0, 0);
texture.LoadImage(binary);
binary = null;
}
return texture;
}
/// <summary>
/// guidからファイルパスへ変換する
/// </summary>
/// <param name="guid"></param>
/// <returns></returns>
private string GetFilePathFromGuid(string guid)
{
if (guid2PathDict == null)
return "";
string value = "";
if (guid2PathDict.TryGetValue(guid, out value))
{
return value;
}
else
{
return "";
}
}
//TODO pngに対応したmetaファイルだけ取得とかできそう
/// <summary>
/// guidとファイルパスのセットを取得する
/// </summary>
/// <param name="rootFolderPath"></param>
/// <returns></returns>
private Dictionary<string, string> GetPairGuidAndFilePath(string rootFolderPath)
{
var dict = new Dictionary<string, string>();
var info = new DirectoryInfo(rootFolderPath);
var files = info.GetFiles("*.meta", SearchOption.AllDirectories);
foreach (var f in files)
{
var yaml = new YamlStream();
using (var file = new StreamReader(f.FullName, System.Text.Encoding.UTF8))
{
yaml.Load(file);
}
var mapping = (YamlMappingNode)yaml.Documents[0].RootNode;
var guid = ((YamlScalarNode)mapping["guid"]).Value;
dict.Add(guid, new Regex(".meta").Replace(f.FullName, ""));
}
return dict;
}
/// <summary>
/// ルートフォルダのパスを取得する
/// </summary>
/// <param name="filePath"></param>
/// <param name="rootFolderName"></param>
/// <returns></returns>
private string GetRootFolderPath(string filePath, string rootFolderName)
{
var regex = new Regex(rootFolderName + ".*");
var path = regex.Replace(filePath, "");
return path + rootFolderName;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment