【Unity】【C#】TGAをランタイムでロードする 
2020/05/31 Sun [edit]
BMPLoader に続き、これも試験的(Experimental)に VRM Live Viewer に導入しているのだが、オリジナルソースに少し手を加えた方が使い易いと思うので、ほんのちょっと改造版を載せておこう。
今回紹介する方法は、オープンソースの TGALoader を使う方法だ。ただ、少し注意点は、「フルカラー・無圧縮」のみ対応という点だ。
それを踏まえた改造版になる。簡単に説明すれば、「ヘッダ部分から画像データ形式を拾って、フルカラーでなければエラーを出す」というものを付け加えたものだ。あと、github にあったコメントなどの修正もついでに入れてある。
・TGALoader.cs (mikezila)
・TGA Loader for Unity3D (aaro4130)
(※) Unity 2019.2.21f1 / Windows10(x64) で確認
●TGALoader.cs(フルカラー・無圧縮のみ) [github のほんのちょっと改造版]
// https://gist.github.com/mikezila/10557162
// This was made by aaro4130 on the Unity forums. Thanks boss!
// https://forum.unity.com/threads/tga-loader-for-unity3d.172291/
// It's been optimized and slimmed down for the purpose of loading Quake 3 TGA textures from memory streams.
using System;
using System.IO;
using UnityEngine;
public static class TGALoader
{
public static Texture2D LoadTGA(string fileName)
{
using (var imageFile = File.OpenRead(fileName))
{
return LoadTGA(imageFile);
}
}
public static Texture2D LoadTGA(Stream TGAStream)
{
using (BinaryReader r = new BinaryReader(TGAStream))
{
r.BaseStream.Seek(2, SeekOrigin.Begin);
int type = r.ReadByte(); //(offset 2) 画像形式 (1バイト)
//フルカラー以外は非対応とする(=RLE圧縮は展開コードが必要のため)
if (type != 2)
{
throw new Exception("Only Full-color Uncompressed is supported.");
}
// Skip some header info we don't care about.
// Even if we did care, we have to move the stream seek point to the beginning,
// as the previous method in the workflow left it at the end.
r.BaseStream.Seek(9, SeekOrigin.Current); //offset 3 から +9 → 12
short width = r.ReadInt16(); //(offset 12) 画像の横幅 (2バイト)
short height = r.ReadInt16(); //(offset 14) 画像の縦幅 (2バイト)
int bitDepth = r.ReadByte(); //(offset 16) 色深度 (1バイト)
// Skip a byte of header information we don't care about.
r.BaseStream.Seek(1, SeekOrigin.Current);
Texture2D tex = new Texture2D(width, height);
Color32[] pulledColors = new Color32[width * height];
if (bitDepth == 32) //アルファあり
{
for (int i = 0; i < width * height; i++)
{
byte red = r.ReadByte();
byte green = r.ReadByte();
byte blue = r.ReadByte();
byte alpha = r.ReadByte();
pulledColors [i] = new Color32(blue, green, red, alpha);
}
} else if (bitDepth == 24) //アルファなし
{
for (int i = 0; i < width * height; i++)
{
byte red = r.ReadByte();
byte green = r.ReadByte();
byte blue = r.ReadByte();
/* (github のコメントから)
https://gist.github.com/mikezila/10557162#gistcomment-2995969
pulledColors [i] = new Color32(blue, green, red, 1); //float で 1.0 と間違えた?
1 need to be replaced by 255, in another case, texture become transparent
*/
pulledColors [i] = new Color32(blue, green, red, 255);
}
} else
{
throw new Exception("TGA texture had non 32/24 bit depth.");
}
tex.SetPixels32(pulledColors);
tex.Apply();
return tex;
}
}
}
ファイルフォーマットに関しては以下を参考にさせて貰った。 このコードではRLE圧縮には対応してない。RLE圧縮に関しては以下の記事にも載っているので、参考にするのも良いだろう。
(参考)
・TGA ファイルフォーマット (GAME PROGRAMMING UNIT)
・TGA ファイルフォーマット (PROJECT ASURA)
今回利用するヘッダ部分と画像のデータ形式だけ抜粋させて頂くと以下のような表になる。
ヘッダー (18 byte)
offset | byte | 内容 |
---|---|---|
0 | 1 | ヘッダーの後に続く、IDの長さ (ID field length) |
1 | 1 | カラーマップの有無 (Color map type) |
2 | 1 | データ形式 (Image type) |
3 | 2 | カラーマップの位置 (Color map index) |
5 | 2 | カラーマップの長さ (Color map length) |
7 | 1 | カラーマップエントリーのビット数 (Color map size) |
8 | 2 | X (Image origin X) |
10 | 2 | Y (Image origin Y) |
12 | 2 | 幅 (Image width) |
14 | 2 | 高さ (Image height) |
16 | 1 | ピクセルのビット数 (Bit per pixel) |
17 | 1 | 属性 (Discripter) |
(offset 2) データ形式 (1 byte)
値(整数) | 内容 |
---|---|
0 | イメージなし |
1 | インデックスカラー(256色) |
2 | フルカラー |
3 | 白黒 |
9 | インデックスカラー。RLE圧縮 |
10 | フルカラー。RLE圧縮 |
11 | 白黒。RLE圧縮 |
まぁ、ここでは「フルカラー・無圧縮」に限定してしまっているが、データ形式にあるように「フルカラー/インデックスカラー/白黒」と「圧縮/無圧縮」と「格納方向:縦/横」(offset 17 の属性)を組み合わせるとコードが長くなるのは容易に想像できるだろう。以下に複数の形式で TGA → BMP に変換するサンプルコードはあったので、興味があったら覗いてみるのも良いかも知れない(そしてRLE展開も作ったら公開して欲しい(笑))。
・TGALoader.cs (openmetaversefoundation/libopenmetaverse)
簡単なテストをするには以下ようなのコードで十分だろう。UI に RawImage を適当に置いて、インスペクタにセットし、読み込む画像のパスを設定してプレイすれば良い。画像が表示できたら成功だ。
●簡単な確認コード
using System;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
public class TGALoaderTest : MonoBehaviour
{
public RawImage image; //画像表示用
public string path; //画像パス
// Use this for initialization
private void Start()
{
LoadTGA(path);
}
void LoadTGA(string path)
{
try
{
var tex = TGALoader.LoadTGA(path);
image.texture = tex;
}
catch (Exception e)
{
Debug.LogError(e.Message);
}
}
}
ちなみにフルカラーのチェックをしないでRLE圧縮形式のTGAを読み込んだ場合は「Unable to read beyond the end of the stream.」というエラーが出る(たぶん圧縮してある場合は、データ長が短くなるので足りなくなり、エラーとなる)。このサンプルコードの場合はフルカラー以外では「Only Full-color Uncompressed is supported.」と出る。必要なら何か処理を入れた方が良いだろう。
また、私が提供しているプラグインを使えば、Android でも TGA を読み込めた。実際にスマホで全天球ビューワを作ることも可能だ(これが VRM Live Viewer で実装されている)。フリーなので、自由に使って欲しい。
(関連記事)
【Unity】【C#】BMP をランタイムで読み込む
【Unity】【C#】ランタイム時にファイルをドラッグ&ドロップして取得する(Windows のみ)
【Unity】スマホで簡易360度(パノラマ, 全天球)ビューワを作る
【Unity】Androidのトーストやダイアログ、通知、音声認識、ハード音量操作など基本的な機能を使えるプラグインを作ってみた
【Unity】AssetStore版 FantomPlugin のセットアップ
- 関連記事
トラックバック
トラックバックURL
→http://fantom1x.blog130.fc2.com/tb.php/354-57550cbe
この記事にトラックバックする(FC2ブログユーザー)
| h o m e |