C#は電気羊の夢を見るか?

VC#とかSharpDXとかUnityの情報をメモするブログです。

Enumerable.Repeatでハマった

Enumerable.Repeatの挙動が想像していたのと違うのでメモ


こんな感じのクラスがあったとする
    public class test

{
public int index;

public test(int n)
{
index = n;
}
}
で、それをEnumerable.Repeatを使って、デフォルトコンストラクタを呼び出す。
    test[] array = Enumerable.Repeat(new test(5), 10).ToArray();
すると、結果はarrayのindexはすべて5となる。
ここまでは良い。

そして、それに値をこんな感じでセットする
    for (int i = 0; i < array.Length; i++)

{
array[i].index = i;
}
すると、結果はarrayのindexは、すべて 9 になってしまう。


何故こんな事が起こるかというと、
Enumerable.Repeatは、指定された回数初期化処理を行うのではなくて、
指定された個数のコピーを作るに過ぎないと言うことっぽい。

これ、別のバグとワンセットになって潜んでいた性で、かなり苦労させられた。
Repeatの名に騙された・・・

InvalidOperationExceptionが出るときの引数を犠牲にしない対処法

InvalidOperationExceptionは他のスレッドから、 コントロールの操作を行おうとすると発生する。 細かいお話は他のサイト様に任せるとして、とりあえずこの例外を切り抜ける方法。 何が起こっているか知りたい人は InvokeRequiredやMethodInvokerあたりを調べると幸せになれると思う。

C#でXMLを扱う時のメモ

XMLを扱っている上で困った、アレコレをまとめてみた。


・クラスをシリアライズしたXMLファイルを読み込む時、
XmlDocumentのLoadでOutOfMemoryExceptionが発生する対処方法


以下のように記述すると、XMLがある一定のサイズを超えると、
doc.LoadでOutOfMemoryExceptionが発生するようになる。


とりあえず、エラーを止めるには記述を以下のように変更すれば良い。


しかし、これだけでは根本的な解決にはならない。
一般的な対応方法は、SQLiteで、おとなしくDBを使うのが結果的に楽で早い。

ところで、肥大化したXMLファイルにjpg等の画像を埋め込んでいないだろうか?
この場合は対処方法がある。


・XML内に画像を埋め込んだ時にサイズが巨大になる対処方法
問題は、jpgやpngで取り込んだはずのデータでも、
すべからく32bitBMP形式で保存されるため、サイズが巨大化する。

これは以下のように、バイナリ化する際にImageクラスをそのまま、バイナリ化するのが原因。

これを解消するには、保存する際にjpgのバイナリにしてやることで回避可能。
もちろん画質はjpgになるが、この対応で読み込み時間が劇的に改善される。

さらにもう一歩踏み込むと、これはメモリ上にはビットマップで持ったままになる為、
プログラムの動作改善にはあまり効果がない。

そこで、使う寸前までJPGのバイナリでメモリ上に保持しておき、使う時に
Image = new Bitmap(new MemoryStream(PictureByteArray))
とした方がメモリ効率がぐっと良くなる。

また、読み込み時のメモリ確保、Bitmapの構築コストが無くなるので、
読み込み時間が更に短くなるはず。


・大きなファイルは直接上書きしない
あと、基礎的な所で、これだけ巨大なXMLを保存するときは直接上書きしてはならない。
以下の実装で書き出し中に万一メモリ不足でプログラムがハングアップすると、
過去データがすべて消える憂き目に合う。



気持ち面倒ではあるが、ワンクッション置くと、万一のとき、被害の度合いがぐっと減る。


以上、XMLアレコレでした。
基本的に、XMLでこのようなメモリをバカ食いする実装は、
本来は望ましくないのではないかと思う。

これらの手法は、ある程度データが増えた場合の一時的な対応なので、
なんらかのDBに移行するために、時間を稼ぐ延命措置ぐらいに思って欲しい。


もしくはXMLをランダムアクセスする、豪快なクラスを作るのも面白いかもしれない。
先頭アドレス、サイズ、キーを保存したファイルを用意すれば、不可能ではないのではないかと思う。

ネットからダウンロードしたアセンブリを読み込む

どうにも思うようにmkbundleが動作してくれないので、いよいよとパッチ作業を始めた。その中で以下のエラーが出て先に進めなくなったのでメモ。

ネットワーク上の場所からアセンブリを読み込もうとしました。これにより、以前のバージョンの .NET Framework で、アセンブリがサンドボックス化された可能性があります。このリリースの .NET Framework では、CAS ポリシーが既定で有効になっていないため、この読み込みは危険な場合があります。この読み込みがアセンブリのサンドボックス化を目的としない場合は、loadFromRemoteSources スイッチを有効にしてください。詳細については、http://go.microsoft.com/fwlink/?LinkId=155569 を参照してください。

要約すると、
『読み込んじゃいけないっぽいアセンブリを読みこもうとしてますぜ、旦那。
どうしてもって言うなら、フラグ変えると幸せになれるかもな。
続きはWebで。』


じゃぁ、続きをと思って見てみると、以下のメッセージが目に入った。
「Visual Studio プロジェクトのエラー一覧に表示されたエラー メッセージ、またはビルド エラーによってこのトピックにたどり着いた場合は、「方法: Visual Studio で Web からダウンロードしたアセンブリを使用する」を参照してください。

要約すると
『お前も、かの場所から来たのか。
ならば、お前の進むべき道は・・・
続きはWebで。』


というわけで続きを開いてみると
方法: Visual Studio で Web からダウンロードしたアセンブリを使用する

あぁ、なるほど。

Unity小ネタ小技集

Unityを始める上でハマったこと、悩んだことをメモ
初心者の書いた内容なので、間違ってる所があったら指摘お願いします。

○VisualStudioで開発する
メニューからEdit→Preferences→GeneralタブのExternal Script Editorから選択

○スクリプトを実行するときの注意点
VSで編集した奴は保存しないと反映されない。

○ステップ実行
VSでは現在のところできない。MonoDeveropならできる

○グローバル変数が表示されない
オブジェクトにグローバル変数(プロパティ)が表示されていないのはPublicになっていないから

○UIをプレビューする
プレビューはクラスの先頭に[ExecuteInEditMode()]をつける

○UIでテクスチャが表示されない
カメラオブジェクトにUIのスクリプトを仕込んでテクスチャを表示しようとすると表示されない。
メニューのGameObject→CreateEmptyで出来上がったオブジェクトに仕込む

○ボタンが文字化けする
VisualStudioのメニューからファイル(F)→名前をつけて〜.csを保存(A)→
上書き保存(S) 横の ▼ を押下→エンコード付きで保存→Unicode - コードページ 1200を選択

○Texture2Dのシリアライズ
標準のTexture2D.EncodeToPNG()でbyte[]を取得可能
Texture2D.LoadImage(byte[])でTexture2Dを取得できる

jpegエンコーダーは海外サイトで見つかる。


○EditorUtilityはビルド後使えない
exメッセージボックス、ファイルダイアログ
SaveFilePanelやDisplayPopupMenuは使えないので自力実装する必要がある


【エラーメッセージ】
○the referenced script on this behaviour is missing!
身に覚えのないスクリプトがオブジェクトに無いかチェック
余分なスクリプトや使わなくなったスクリプトがのこってる場合がある

○unity Unexpected symbol `'
コメントの前とかに全角空白が残ってるとエラー

○NullReferenceException UnityEngine.Camera.ScreenToWorldPoint
カメラのtagにメインカメラの指定が必要

【Tips】
○String.EmptyボタンはiPhoneで重い
1*1の透明テクスチャボタンを使う

mkbundleで発行されるgccのオプション

普通にcygwinからmkbundleをやるのもつまらないので
MinGWとMSYSをつかいcmd.exeだけで処理を完結させようかと思う。


そのため、gccのコマンドを自力発行するべく
mkbundleで発行されるオプションと、
使えそうなオプションを調べてみた。

●書式
gcc「リンカへ渡すオプション」
-o「実行ファイル名」
「コンパイルオプション」
「ソースファイル名」
「オブジェクトファイル名」

●mkbundleで発行されるgccコマンド
○gcc

○-mno-cygwin
cygwin1.dllを必要としないバイナリを作る

○-g
デバッグ情報を生成する

○-o NAME
作成するバイナリの名称を指定

○-Wall
警告を表示する

○temp.c
ソースコード

○`pkg-config --cflags --libs mono-2|dos2unix`
・オプション文字列の自動生成 リンク -lフォルダ名を列挙
CFLAGS=`pkg-config --cflags mono-2`
LDFLAGS=`pkg-config --libs mono-2`

中身はオプションの情報をまとめたもの。

pkg-configコマンドを利用して展開すると以下の様な感じ
(今回は直接MinGWを利用するので-mno-cygwinは全て不要)

dos2unixは改行コードを[CR+LF] -> [LF]とする
(これも同様に不要)

○temp.o
オブジェクトファイル名
(DLLとかもここに書く)


●使用されていないけど、使えそうなオプション
-shared DLL作成用オプション(リンカオプション)
-s オプションでファイルサイズを減少させられる らしい(リンカオプション)
-O1 オー 最適化オプション(リンカオプション)
-O2 高度最適化
-O3 超高度最適化
-O0 最適化を行わない
(最適化のデフォルトは不明)

最適化の詳細はこちらのサイトが詳しい
http://www.cqpub.co.jp/interface/column/freesoft/2002/200208/08-4.htm

Win32用のmkbundleのオプションを、とりあえず訳してみた

Monoを本格的に利用するためにWin32用のmkbundleのオプションを、とりあえず訳してみた。

SlimDXの問題点とSharpDX

ある程度SlimDXでの開発を終え、テストもボチボチ進んだ頃、
「さぁ、仲間内に配布」という所で有る問題が持ち上がってきた。

それは、「必要とするライブラリが多い」という事実。

もともとXNAからSlimDXへ移行したのは
実行するユーザーの負担の少なさを期待してのものだったのだが、
これが外れたのだ。

.Netはそもそも標準インストールではないので、
インストールされていないPCにはインストールが必要となる。
そしてSlimDXはC++ Runtimeが必要なのでこれも必要となる。

結局、これだとユーザーに付随するライブラリを2つ以上要求することになる。
ダメだったときに「.Net入れてね」だけで済まないのだ。

.Netだけであれば「わりと入っているだろう」という楽観の下
.Net 2.0環境で開発を続けていた。
ところが、そうは行かなかった訳だ。


困っていたところに、面白いものを見つけた。
SharpDX
http://code.google.com/p/sharpdx/

これの良いところは
・もちろんC++ Runtimeが不要
・Mono上で動作する(.NET Frameworkも不要)
・SlimDXより高速らしい

悪いところは
・日本語の情報がない


言語の壁があるが、
ある程度対応するAPIが有るであろうことを期待して、
SlimDXから移行をしてみようかと思う。

使い捨てメアドを本当に使い捨てる

取得に情報入力が不要で、破棄も自動的。
本当にページを表示してすぐに使えるメアドサービス。

その名も「10分メアド」
http://10minutemail.com/10MinuteMail/

前は英語のみだったんだけど、いつの間にか日本語に対応してた。


使い捨てメアドを登録すると、そのサイトからメールが来たり、
結局ゴミを片付けるなどのメンテが必要だったりして、
「使い捨て」できていないメアドが増えてきてしまってたのだが、
これで、そういう煩わしさからおさらばできる。

Unityテスト

Unityをブログに貼れないかなぁと模索
続きを読むで表示されるはず。

一回で髪の毛の揺れが止まるのは仕様です。
読み込み終わってからワンテンポ遅れて表示される、はず。

続きを読む »

SlimDXでXAudio2をつかってサイン波を再生する

XAudio2のいろんなサンプルを継ぎ接ぎしてSlimDXに対応させた。


一番困ったのは、転送してるのはByteのくせに、
その中身はShortにしておく必要があるとか、
よく分からない事になってたのに気がつかなくて苦労した。

[2011/11/27追記]
format.BitsPerSample = 16; なんだから当然でした。
24とか32Fによって対応方法を変えてくりゃれ。

32Fはfloatに一旦突っ込んで中身が1.0〜-1.0って表記になる
そいつを、int32に放り込むなら32bitの最大値で掛けて、整数型に直してやればいい。

この方法だと、厳密にはプラス側と、マイナス側で最大1の誤差が出るが、
どう頑張っても聞き分けられないので、無視していいと思う。
[追記終わり]


byte[] byteArray = new byte[waveData.Length * 2]; //バッファ
Buffer.BlockCopy(waveData , 0, byteArray, 0, byteArray.Length);

↑このへん。

それ以外はWaveを扱ったことがある人なら、それほど苦労することはないと思う。
若干クセがあるものの、全体的に簡単な使い心地。
というか、他の音関係のライブラリが難しすぎるんだと思う・・・

そんなわけで、以下サンプル。


XAudio2 device = new XAudio2();
MasteringVoice masteringVoice = new MasteringVoice(device);

WaveFormat format = new WaveFormat();
format.Channels = 1;
format.SamplesPerSecond = 44100;
format.BitsPerSample = 16;
format.BlockAlignment = (short)(format.BitsPerSample / 8 * format.Channels);
format.AverageBytesPerSecond = format.SamplesPerSecond * format.BlockAlignment;
format.FormatTag = WaveFormatTag.Pcm;

// Create the sample data into memory
short[] waveData = new short[format.AverageBytesPerSecond];

//「ラ」を表すsin波(440Hz、1秒)
for (int i = 0; i < waveData.Length; i++)
{
waveData[i] = (short)(32767.0 * Math.Sin(2.0 * Math.PI * 440.0 * i / (double)format.SamplesPerSecond));
}

byte[] byteArray = new byte[waveData.Length * 2]; //バッファ
Buffer.BlockCopy(waveData , 0, byteArray, 0, byteArray.Length);

MemoryStream ms = new MemoryStream(byteArray);
AudioBuffer buffer = new AudioBuffer();
buffer.AudioData = ms;
buffer.AudioBytes = (int)ms.Length;
buffer.LoopCount = XAudio2.LoopInfinite; // 永久にループ
buffer.LoopBegin = 0; // ループ開始位置
buffer.LoopLength = 0; // 再生する長さ。0だと全部。
buffer.Flags = BufferFlags.EndOfStream;

SourceVoice sourceVoice = new SourceVoice(device, format);
sourceVoice.SubmitSourceBuffer(buffer);
sourceVoice.Start(); //再生

MessageBox.Show("終了");

// cleanup the voice
buffer.Dispose();
sourceVoice.Dispose();
ms.Close();
ms.Dispose();

masteringVoice.Dispose();
device.Dispose();

DirectShowLib-2005をWin7で使えるようにする

DirectShowLib-2005をWindows7の64bitで使おうとしたところ
『接続に必要な中間フィルターの組み合わせが見つかりませんでした。』
とかってエラーがでて動作しない。


Windows7ではDirectShowフィルターは
強制的にMicrosoft製の物が優先になるとか何とか(うろ覚え)

まぁ、そのあたりの絡みなのかなぁと思いつつ、
どうせ使わないのでDirectShowフィルターを無効にしてやることで、
とりあえずこの問題をクリアした。


修正はたった1行で
ソース『Capture.cs』内の SetupGraph メソッド中にある、
以下の一行をコメントアウトすればよい。

m_pinStill = DsFindPin.ByCategory(capFilter, PinCategory.Still, 0);

シェーダー高速化の肝

今まではXNAのサンプルそのままに
ガウシアンブラーで右方向と下方向をサンプリングしていたのだが、
これを右下、左上に変更するだけで、なぜか高速化されたので覚書き。

TexCoordのサンプラオフセット値の指定を
texcoord + float2(x,0);
texcoord + float2(0,y);
から
texcoord + float2(x,y);
texcoord - float2(x,y);
と変えるだけで780->920fpsという、となかなか大き変化があった。

時間に直せば0.0002秒の差だが、
低速なマシンではボチボチ効果がある差なのではないかと思う。

一体何がどうしてこの速度の差に現れたのか、すごく気になる。
そこで、あれこれ試してみたところ、
少しコードの書き方を変えるだけで30fpsくらいは、ぴょこぴょこと簡単に変化が出る。

シェーダにおいてはコンパイラさんが気に入るコードを書くことが、
高速化の肝なのだなぁと身を持って感じた。

SlimDXでeffectが使える2D描画の方法

偉そうに書いているが、TEXCOORD周りが厳密には間違っている気がする。
見たところの動作は良好。

この実装では必ずClientサイズとTextureサイズが等しいこと。
RenderTargetから値を持ってきた時を想定しているので、この形に。

Effectまわりは自分で追加してくりゃれ。


[StructLayout(LayoutKind.Sequential)]
struct Vertex
{
public const VertexFormat Format = VertexFormat.Position | VertexFormat.Texture1;

public Vector3 Position;
public Vector2 TextureCoordinate;
}

public partial class Form1 : Form
{
// Direct3D9 Device
static Device _device;
// テクスチャ
static Texture _texture;
// テクスチャ描画用の頂点バッファ
static VertexBuffer _vertices;

public Form1()
{
InitializeComponent();

var pp = new PresentParameters();
pp.BackBufferWidth = this.ClientSize.Width;
pp.BackBufferHeight = this.ClientSize.Height;

_device = new Device(new Direct3D(), 0, DeviceType.Hardware, this.Handle, CreateFlags.HardwareVertexProcessing, pp);

// カラー成分の設定
_device.SetTextureStageState(0, TextureStage.ColorOperation, TextureOperation.SelectArg1);
_device.SetTextureStageState(0, TextureStage.ColorArg1, TextureArgument.Texture);
// アルファ成分の設定
_device.SetTextureStageState(0, TextureStage.AlphaOperation, TextureOperation.SelectArg1);
_device.SetTextureStageState(0, TextureStage.AlphaArg1, TextureArgument.Texture);
// ブレンディングモードの設定
_device.SetRenderState(RenderState.AlphaBlendEnable, true);
_device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
_device.SetRenderState(RenderState.DestinationBlend, Blend.InverseSourceAlpha);


// テクスチャを sample.png から読み込む
_texture = Texture.FromFile(_device, "test.bmp", 0, 0, 1, Usage.None, Format.A8R8G8B8, Pool.Managed, Filter.None, Filter.None, 0);

var description = _texture.GetSurfaceLevel(0).Description;

// 頂点のサイズ (バイト)
int vertexSize = Marshal.SizeOf(typeof(Vertex));
// 頂点バッファを作成
_vertices = new VertexBuffer(_device, 4 * vertexSize, Usage.WriteOnly, Vertex.Format, Pool.Managed);
// 頂点バッファをロック
var dataStream = _vertices.Lock(0, 4 * vertexSize, LockFlags.None);

// テクスチャ座標の指定
float x1 = 0.5f / description.Width;
float y1 = 0.5f / description.Height;
float x2 = (pp.BackBufferWidth + 0.5f) / description.Width;
float y2 = (pp.BackBufferHeight + 0.5f) / description.Height;

// 頂点バッファにデータを書き込む
dataStream.WriteRange(new[] {
new Vertex { Position = new Vector3(-1.0f, 1.0f, 0.0f), TextureCoordinate = new Vector2(x1, y1) },
new Vertex { Position = new Vector3(1.0f, 1.0f, 0.0f), TextureCoordinate = new Vector2(x2, y1) },
new Vertex { Position = new Vector3(-1.0f, -1.0f, 0.0f), TextureCoordinate = new Vector2(x1, y2) },
new Vertex { Position = new Vector3(1.0f, -1.0f, 0.0f), TextureCoordinate = new Vector2(x2, y2) },
});

// 頂点バッファをアンロック
_vertices.Unlock();
}

public void Run()
{
SlimDX.Windows.MessagePump.Run(this, Draw);
}

void Draw()
{
// 画面をクリアする
_device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.CornflowerBlue, 1.0f, 0);


_device.BeginScene();

// 描画する頂点バッファを指定する
_device.SetStreamSource(0, _vertices, 0, Marshal.SizeOf(typeof(Vertex)));

// 頂点バッファのフォーマットを指定する
_device.VertexFormat = Vertex.Format;

// テクスチャを指定
_device.SetTexture(0, _texture);

// 描画する
_device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2);

_device.EndScene();

// 表画面に描画内容を転送
_device.Present();
}

超ニッチなエクセルグラフの作り方

一体どこに需要があるかさっぱり解りませんが、
「2軸上の折れ線と積み上げ棒グラフ」の作成と、
その調整方法をメモ


グラフ作成時に[ユーザー設定]→[2軸上の折れ線と縦棒]から
[2軸上の折れ線と積み上げ縦棒]にする方法は、
適当な棒グラフを右クリック→「グラフの種類」→「縦棒」→「積み上げ縦棒」とすればOK

ここから、折れ線グラフになってしまった値は、
対象の折れ線グラフを右クリック→データ系列の書式設定→軸のタブ→第2軸から主軸
さらに、棒グラフを右クリック→「グラフの種類」→「縦棒」→「積み上げ縦棒」とすればOK

そこからさらに順番を変更したい場合は
適当な棒グラフを右クリック→データ系列の書式設定→系列の順序タブ→上へ移動、下へ移動ボタン


軸を調整せずに見た目だけ変更していると、この順番が変更できず、
まるで項目が表示されていないように見えるのでハマった。

SlimDX.D3D9を使いGPUで描画した結果をCPUで取得する

相変わらず海外を含めてサンプルが見つからない。

SlimDXでGPGPUをDX9のまま実装するため
描画済みのテクスチャを取得する方法を置いとく

描画メソッド内に以下の内容を実行すると、取得ができる。

テスト用のメソッドなので、ビットマップの保存や、
巨大な配列の宣言とかは適宜修正してくりゃれ。


//レンダーターゲットを用意する
renderTexture = new Texture(device, this.ClientSize.Width, this.ClientSize.Height, 1, Usage.RenderTarget, Format.X8R8G8B8, Pool.Default);
renderSurface = renderTexture.GetSurfaceLevel(0);

//レンダーターゲットを保存する
oldRenderSurface = device.GetRenderTarget(0);

//レンダーターゲットを変更する
device.SetRenderTarget(0, renderSurface);

//画面を初期化
device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0);
device.BeginScene();

//データを流しこんで描画
device.SetStreamSource(0, vertices, 0, Marshal.SizeOf(typeof(Vertex)));
device.VertexDeclaration = vertexDecl;
device.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);

//レンダーターゲットを元に戻す
device.SetRenderTarget(0, oldRenderSurface);
oldRenderSurface.Dispose();

//ストリームを用意
DataStream stream = Texture.ToStream(renderTexture, ImageFileFormat.Bmp); //ファイルストリームと等価

//ビットマップとして流しこむ
Bitmap bmp = (Bitmap)Bitmap.FromStream(stream, false, false);

//内容を保存して確認
bmp.Save("test.bmp", System.Drawing.Imaging.ImageFormat.Bmp);

//直接バイト型として参照も可能
byte[] bs = new byte[stream.Length / sizeof(byte)];
stream.Read(bs, 0, bs.Length);

device.EndScene();
device.Present();



はっきり言って暗中模索のなか、どうにか実行できているレベルなので、
このままでは問題が起こる可能性が多分にある。

メモリリークの可能性や、その他何か気づいた点があったら、
遠慮なくコメントからツッコミをお願いします。


蛇足:
国内のサンプルは何故かテクスチャを
一生懸命BitmapクラスやらImageクラスからメモリ通して作ってるけど、
なんで
Texture tex = Texture.FromFile(device, filePath);
を使わないんじゃろうか・・・

低速であっても、どうせ初期化の一回きりの処理だし、
気にならないと思うんだけど、何か理由があるんだろうか・・・

Windows7でWindowsXPのペイントを使う

かなり高機能なったWindows7のmspaint

しかし、使い慣れない部分も多く、あの機能がなくなったなぁ・・・
と思うことも多い。
なんか、この行はどこかで見たような気がするが気にしない。

そこで、使わなくなった正規XPのCDを破棄したり、中古でPCショップに渡さず、
XPをインストールしていた時にバックアップをしたmspaint.exeさんを
Windows7マシンにコピーし実行しましょう。

すると、ちゃんと懐かしのあの画面が出てきます。
機能的な制限もなく、exe一つで簡単に実行できます。


HDDがクラッシュして使わなくなった古いPCからリカバリCDをもらったりすれば、
権利的にもグレーにはなると思う。

くれぐれも友人のパソコンや学校、職場のパソコンからコピーだけしないように。
ホログラムに輝く正規CDを胸に行うこと。


ちなみにXPのバージョンである5.1は、ほぼそのまま使えるのですが、
2000のバージョンである5.0はjpeg保存ができなくなります、トホホ。

密かなるSlimDX

最近SlimDXを始めた。

んが、しかし。
XNAとぜんぜん使い勝手が違う。
なんていうか、ラップが薄く、”楽”じゃないなぁという感じ。

しかも!なかなかと変な癖があって
Vector3.Transformの戻り値がVector4だったり、
Colorの持ち方がint型だったり、そこかしこに罠が潜んでいる。

そのあまりの違いに、まともにモデルを表示することもままならず、
途方にくれていた。


その原因はとにかくサンプルがない。
情報の殆どは英語と、なかなか敷居が高い。

たかだかインデックスバッファのサンプルを探しても、
どうやら存在しないのだから泣けてくる。

仕方ないのでDirectXのサンプルやらXNAの記憶とかを便りになんとか、
シンプルなIndexBuffer描画サンプルを作成したので、ここに置いておく。



実行結果


using SlimDX;
using SlimDX.Direct3D9;
using SlimDX.Windows;

using System.Runtime.InteropServices;

namespace IndexbufferTest
{
public partial class Form1 : Form
{
Device device;

public struct TestVertex
{
public Vector3 Position;
public Vector3 Normal;
public int Color;
}

VertexElement[] vertexElementsArray = new VertexElement[]
{
new VertexElement(0, 0, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Position, 0),
new VertexElement(0, 12, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Normal, 0),
new VertexElement(0, 24, DeclarationType.Color, DeclarationMethod.Default, DeclarationUsage.Color, 0)
};

Vector3[] vectors = new Vector3[] { new Vector3(-1, 0, 0),
new Vector3(0, 1, 0),
new Vector3(1, 0, 0),
new Vector3(0, -1, 0)
};

public Form1()
{
InitializeComponent();

var pp = new PresentParameters();
pp.BackBufferWidth = this.ClientSize.Width;
pp.BackBufferHeight = this.ClientSize.Height;
pp.DeviceWindowHandle = this.Handle;

this.device = new Device(new Direct3D(), 0, DeviceType.Hardware, this.Handle, CreateFlags.HardwareVertexProcessing, pp);
}

public void Run()
{
SlimDX.Windows.MessagePump.Run(this, Draw);
}

protected virtual void Draw()
{
int vertexSize = Marshal.SizeOf(typeof(TestVertex));

VertexBuffer vertexBuffer = new VertexBuffer(device, vectors.Length * vertexSize, Usage.None, VertexFormat.None, Pool.Managed);

var vertexData = new TestVertex[vectors.Length];
for (int i = 0; i < vertexData.Length; i++)
{
vertexData[i].Position = vectors[i];
vertexData[i].Normal = new Vector3(0, 0, -1);
vertexData[i].Color = Color.Gold.ToArgb();
}


IndexBuffer indexBuffer = new IndexBuffer(device, vectors.Length * sizeof(uint) * 3 - 2, Usage.None, Pool.Managed, false);
var indices = new uint[]{0, 1, 2,
0, 2, 3};

device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.CornflowerBlue, 1.0f, 0);
device.SetRenderState(RenderState.Lighting, false);
device.BeginScene();

device.VertexDeclaration = new VertexDeclaration(device, vertexElementsArray);

DataStream vertexBufferStream = vertexBuffer.Lock(0, vectors.Length * vertexSize, LockFlags.None);
vertexBufferStream.WriteRange(vertexData);
vertexBuffer.Unlock();
device.SetStreamSource(0, vertexBuffer, 0, vertexSize);

DataStream indexBufferStream = indexBuffer.Lock(0, indices.Length * sizeof(uint), LockFlags.None);
indexBufferStream.WriteRange(indices);
indexBuffer.Unlock();
device.Indices = indexBuffer;

device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vectors.Length, 0, indices.Length / 3);

device.EndScene();
device.Present();
}
}
}

これは便利だ・・・

超基本なんですが、今までずーっとこれで苦労していた。

タブ遷移

こいつを自動的にささっと指定する方法はないかと、
何度も探しては挫折していたのですが、
ようやく見つかった。


メニューバー[表示(V)]→タブオーダー(V)としてから、
遷移させたい順にオブジェクトをクリック
そこからメニューバー[表示(V)]→タブオーダー(V)で確定


今まではそれほど画面数がなかったので、
我慢して自力でやってましたが、いつからこの機能あったんだろう・・・

確か、前の会社でメジャーバージョンアップ時にタブオーダーの変更で、
2人日くらいスケジュールがとったような記憶が・・・・・・(汗

知らんかった・・・

bool型って初期値があって、宣言すれば勝手にfalseになるそうな。
コンストラクタで一生懸命初期化してたよ・・・

VC#2010EEで対象のフレームワークが変更できない時の対処方法

現在XNA4.0化を進めていたのですが、いくつかハマったので情報として残しておく。


参照アセンブリ "Microsoft.Xna.Framework.Content.Pipeline,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553" は、
現在のターゲット フレームワーク ".NETFramework,Version=v4.0,Profile=Client" 内にない
"Microsoft.Build.Utilities.v4.0, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a" に依存するため、解決できませんでした。
ターゲット フレームワーク内にないアセンブリへの参照を削除するか、
プロジェクトを再ターゲットしてください。

上記エラーメッセージが表示された時の対処方法として
XNA4.0ではデフォルトがおそらく
[.NET Framework 4 Client Profile]なので
これを[.NET Framework 4]としたい。

しかし、VC#2010 Expressだと
『プロジェクトプロパティ→アプリケーションタブ→対象のフレームワーク』から変更ができない。

そこでコンパイルターゲットをExpress Editionで変更するのと同じ方法で、
エラーが出ているプロジェクトファイル(*.csproj)を適当なテキストエディタで開き
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
となっているのを
<TargetFrameworkProfile></TargetFrameworkProfile>
と変更すれば良い。

蛇足として、この方法では『無印→Client』の逆方向への変換はできなかった。


XNA4.0化を対応していると、全体的に煩わしさを取り除くことに注力しているなぁという印象。
その経緯には携帯端末への対応のため、どうしても処理を単純化せざるを得なかった背景もあるように思う。

しかし、この単純化が原因でDirectXにほど近い機能のいくつかが削除されてしまった。
最も痛手だったのはFillMode.Pointの廃止。
これを利用して頂点シェーダからGPGPUを実装していただけに、完全に足元を失ってしまった。
どうにか実現できないかと、あれこれやってみたが、どうにもダメっぽい。

DirectXからFillModeの設定変更を無理やりラップしてみて、
ダメそうならSlimDXへの移行を検討する予定。

Windows7で64bit版WindowsMediaEncorder9を使う

事の始まりは、自分の周りが「TERAやるぞ!」と騒ぎ出し、
「じゃぁ、とりあえず・・・」と自分も持っていたアカウントでベータの抽選にとりあえず登録した。
そしたら自分だけ当選するという事態に見舞われ、配信しろ!と言う事になった。

しかし、不特定多数への配信はまずかろうということで、
何年かぶりにWMEのお世話になることになった。

ところが、ニューマシンは64bitのWin7。
案の定つまずきまくったので配信にいたるまでの方法をメモっとく。


まず、以下の二つを用意

Windows Media Encoder 9 Series x64 Edition
http://www.microsoft.com/downloads/ja-jp/details.aspx?FamilyID=cc41218d-7e37-4546-bf0b-1276959ee3ef

SCFH DSF
http://mosax.sakura.ne.jp/fswiki.cgi?page=SCFH+DSF



設定を終え、さぁ起動というところで以下のエラーが出た場合の対処
パラメータが間違っています(0x80070057)



問題はwmex.dllがシステムに登録されていないのが原因ということで
regsvr32にて登録を行うのだが、
regsvr32 "C:Program FilesWindows Media ComponentsEncoderwmex.dll"
というような半角空白を含むパスは、レガシーな指定方法の名残で指定できない。

そこで
regsvr32 "%programfiles%Windows Media ComponentsEncoderwmex.dll"
というような方法で指定ができるとのこと。
多分デスクトップとかシステムフォルダの一部も応用で指定できるんだろうなぁと思う。


しかしながら、これも実行すると以下のエラーが出る

モジュール"C:Program FilesWindows Media ComponentsEncoderwmex.dll"は読み込まれましたが、DllRegisterServerへの呼び出しはエラーコード 0x80070005 により失敗しました。
この問題の詳細については、エラーコードを検索語として入力してオンラインで検索してください。



これは管理者権限を持たないレベルでコードが実行されたかららしいので、面倒だが、スタートメニューより
すべてのプログラム→アクセサリ→コマンドプロンプト と辿り
コマンドプロンプトを右クリック”管理者として実行”で起動する。
そして先程のコマンドをペーストしてやればようやく登録が完了する。


よしこれでOKかと思いきや、まだダメ。

ソース 'ソース 1'のセットアップ中に次のエラーが発生しました。
パラメータが間違っています。(0x80070057)


等というエラーが出た場合、そいつはステレオミックスが無いからとのこと。
あぁ、そういえばWindows付属標準ドライバで放置したままだったので、最新のメーカードライバを入れてステレオミックスが・・・無い!


しまった、非対応のモデルだったのかと思いきや、
同じマザーでWindowsXPでは出来てるっぽいブログを発見・・ドライバが違った?

とよくよく調べてみればWindows7では、これを標準で隠匿するそうな・・・なんでそんな仕様にした。


そんな訳で以下の画像を参考に設定してくりゃれ。




これでようやくできたよ・・・長い道のりだった。

とうとう来た

待ちに待ったKinect for Windows SDK Betaがようやく登場。
StillDesign.PhysX.NetもPhysx3.0の対応がボチボチと進んでいるようで、
この二つで何かおもちゃ作りたいなぁ・・


しかしながら、KinectにはWindows7しばりがあって、しかも両方共XNAは4.0のみ・・・
C#のAndroidアプリ開発環境MonodroidもVS2010のプラグインだったし、
WinXP+VS2008はもう潮時をとうに超えたんだなぁという実感が湧いてきた。


Win7マシンはもう既にあるから、あとはVS2010か・・・
バージョンアップかぁ・・・高いんだよなぁ(´・ω・)

事の顛末

今回騒ぎに騒いだRadeon問題にようやく決着。

まず結論から言うと、シェーダーのバージョンを変えるとだめになる問題は
Spritebatchで使うエフェクトのコンパイラの指定が、
ピクセルシェーダーにしか適応できないことに起因しているように思う。

というのもコンパイラで3.0のシェーダーを利用するためには
ピクセル、バーテックスシェーダーの両方のバージョンを3.0に揃える必要がある。

これに違反するため、Radeonでは素直にバグとして発症し、
Geforceではドライバにより補正されたという事のようだ。


あと、ポリゴンが伸びる問題があるんだけど、
これはソフトとかいう以前の問題だったっぽくて、
公式のテクスチャスキニングのサンプルでも同様の結果を得た。
(これはちゃんと最新版のXNA4.0で検証した結果)

そういえばFFをやってる時も時折チラツキがあったが、これが原因だったように思う。



SSでは、かかとやつま先が少々伸びている程度だが、
ひどいときは画面いっぱいにポリゴンが伸びることもある。

○飽くまで傾向として
・何故か伸びるのは足元が多い(腕が伸びることもあるがまれにしかない)
・伸びるアニメーションがだいたい一緒
・だいたい2,3ループに一回発症する
・一回伸びると何フレームか連鎖してダメな事が稀にある


たぶん、他のPCゲームやってる所とかでドライバ設定のノウハウありそうなので、
解決法はそっちを当たってみる事にする。

とりあえず、ソフト的にはバグではないと考えてよさそうなので、
この件はこれにてクローズとしたい。


=6/16追記=
ドライバの設定についてはこの現象の解消方法として
BIOSでAdvancedタブ→JumperFree Configurationを選択
PCIE Spread SpectrumをAuto→Disbledに変更といったノウハウがあったが、
自分のマザーボードではこの設定ができなんだ(´・ω・)

さて、どうしたものか・・・

誰か教えてOTL

大ハマリしていたRadeon問題にまた新たな展開が。

自分の理解ではHLSLは完全な下位互換があるのだと思っていた。

だから一つのプログラム内のHLSLによって
バージョンががたがたなのは望ましくないものだと考えた私は
コンパイラの指定を全て3.0に書き換えたのだ。


これを
PixelShader = compile ps_2_0 PixelShader();
こんな感じ
PixelShader = compile ps_3_0 PixelShader();


たったこれだけで、Radeonで描画に失敗するようになる。
つまりXNAでは画面が紫色のままになる。

HLSL内部を詳細にステップ実行したわけではないが、
どうも描画済みレンダーターゲットのテクスチャを取得できず、
その後の処理に失敗してるっぽい。


ハード依存なので人によっては再現しないかもしれないが
公式のブルームサンプルでもシェーダーのバージョンを
全て3.0に変えたところ同様の問題が確認できた。

ちなみに2.0指定では問題なく動作する。


コンパイラが別物になるから、吐き出されるバイナリが変わるなんてのは
頭じゃ理解できるが、動作しなくなる仕組みが解らない・・

なんのなさコレ。
ダレか内部仕様に詳しい偉い人、教えて・・・OTL

 | HOME |  NEXT »

PAGE TOP ▲

Appendix

■春条

■春条

生息地は愛知
車と甘い物が好きな30歳
特技は無限昼寝

=近況=
MONOでSharpDXを使いつつ、Unityでお仕事してます。


Twitterボタン
Twitterブログパーツ

Links

Search

Calender

« | 2012-05 | »
S M T W T F S
- - 1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31 - -

Recent Entries

ホットなオークション!
DTIブログ
ブログでアフィリエイト
DTIブログポータルへ
このブログを通報
Report Abuse