ODE講座22:ODEでポリゴンを簡単に表示する方法

ODE texture

Archive3D.netからダウンロードした3DSフォーマットの「うさちゃん」。lib3dsライブラリを使うことでODE付属のdrawstuffで表示できました。

早いものでもう11月ですね。卒業研究や修士研究に取り組んでいる方はお尻に火か着くころではないでしょうか。

さて、ODE (Open Dynamics Engine)講座の22回目です。ODEに付属している3次元グラフィクスライブラリdrawstuffでは3Dモデルのファイルを読み込みません。ひびきのさんはXファイル(あの謎が謎を呼ぶTV番組ではありません。最も最近はPrison Breakに夢中ですが…)を読み込むX File Loaderを公開しています。 ここではもう一つの標準的な3Dファイルである3DSを読み込むサンプルプログラムをテスト公開します。

3dsファイルを読み込むライブラリとしてlib3dsを使います。これはJan Eric Kyprianidisさんが開発したものでLGPLで公開されています。lib3dsのウェブサイトはここです。

まず、lib3dsをインストールしましょう。方法はODEと同じで 以下の要領です。MSYS+MinGW+Windows Vista環境で問題なくできました。

  • ./configure
  • make
  • make install

後は、このode3dsloader-0.0.tgzファイルを次のリンクからダウンロードして、

今までのサンプルプログラムと同じように展開し、make、実行してください。ただし、このプログラムは完全ではなく、複数の部品で構成されるモデルを表示しようとするとエラーになります。また、テクスチャも表示できません。そのためにはdrawstuffのソースを変更しなければなりません。今後の宿題とします。

なお、3dsファイルを表示するためには、そのファイルが必要になります。上図のうさちゃんArchive3D.netここからダウンロードしました。

以下にソースコードの主要部分を掲載します。3dsファイルを読み込む関数がload3dsModel()です。読み込みが終わると頂点配列Vertices[]とインデックス配列Indices[]にデータが格納されるので、後はODE付属のdemoプログラムdemo_moving_trimesh.cppと同じようにプログラムするとポリゴンデータを扱うことができます。

つまり、一般ユーザはload3dsModel()を一行入れるだけで3dsファイルを読み込むことができます。簡単ですね。

まだ、テスト公開中で不完全です。複数のオブジェクトで構成される3dsデータを表示できなければなりません。コメント頂ければありがたいです。


[code]
// main.cppから抜粋 by Kosei Demura 2007-11-1
//
#include
#include
#include “ode3dsloader.h”
extern int MeshCount; // オブジェクト数

void makeModel()
{
static dReal weight = 10.0;
dReal x0 = 0, y0 = 0, z0 = 1.0;
dMass m;

// 3dsファイルをロードする関数。引数は3dsファイル名
load3dsModel(“../models/rabbit.3DS”);

TriData = (dTriMeshDataID *) malloc(MeshCount * sizeof(dTriMeshDataID));

model = (MyObject *) malloc (MeshCount * sizeof(MyObject));

for (int i = 0; i < MeshCount; i++) {
// rigid body
model[i].body = dBodyCreate(world);
dBodySetPosition(model[i].body, x0, y0, z0);

// for a trimesh object
TriData[i] = dGeomTriMeshDataCreate();

dGeomTriMeshDataBuildSingle(TriData[i], trimesh[i].vertices, 3 * sizeof(float), trimesh[i].vertexCount, trimesh[i].indices, trimesh[i].indexCount * 3, 3 * sizeof(int));

model[i].geom = dCreateTriMesh(space, TriData[i], 0, 0, 0);

// remember the mesh’s dTriMeshDataID on its userdata for convenience.
dGeomSetData(model[i].geom, TriData[i]);

dMassSetTrimeshTotal(&m, weight, model[i].geom);
dGeomSetPosition(model[i].geom, -m.c[0], -m.c[1], -m.c[2]);
dMassTranslate(&m, -m.c[0], -m.c[1], -m.c[2]);

dGeomSetBody(model[i].geom, model[i].body);
dBodySetMass(model[i].body, &m);

dMatrix3 Rotation;
dRFromAxisAndAngle(Rotation, 0, 0, 1, -M_PI/4);
dBodySetRotation(model[i].body, Rotation);
}
}

[/code]

[code]
// ode3dsloader.cpp by Kosei Demura 2007-11-01
// 3dsフォーマットで作られたオブジェクトを読み込むプログラム
// このファイルでload3dsModel()関数を実装している。
// Todo:1つの3Dモデルが複数のオブジェクトで構成されているとエラーになる。
// lib3dsライブラリが必要http://lib3ds.sourceforge.net/
#include “ode3dsloader.h”
#define BASE_LENGTH 1.0 // メッシュモデルのバウンディングボックスの最大長さ[m]
#define MAX_OBJECTS 1000 // オブジェクトの数

typedef struct
{
float *vertices;
unsigned int vertexCount;
int *indices;
unsigned int indexCount;
} MyTrimesh;

extern MyTrimesh *trimesh;

dReal MIN_X = dInfinity, MAX_X = – dInfinity;
dReal MIN_Y = dInfinity, MAX_Y = – dInfinity;
dReal MIN_Z = dInfinity, MAX_Z = – dInfinity;
dReal ADJUST;
Lib3dsFile *file=0;

int MeshCount = 0;

int vCount[MAX_OBJECTS], iCount[MAX_OBJECTS];

void calModel(Lib3dsMesh *mesh, int *points, int *faces)
{
*points = mesh->points;
*faces = mesh->faces;
}

int meshDump(int no, Lib3dsMesh *mesh)
{
if (mesh->points == 0) return 0;

for (int i=0; i< (int) mesh->points; i++) {
trimesh[no].vertices[3*i] = mesh->pointL[i].pos[0];
trimesh[no].vertices[3*i+1] = mesh->pointL[i].pos[1];
trimesh[no].vertices[3*i+2] = mesh->pointL[i].pos[2];
if (MIN_X > mesh->pointL[i].pos[0]) MIN_X = mesh->pointL[i].pos[0];
if (MIN_Y > mesh->pointL[i].pos[1]) MIN_Y = mesh->pointL[i].pos[1];
if (MIN_Z > mesh->pointL[i].pos[2]) MIN_Z = mesh->pointL[i].pos[2];
if (MAX_X < mesh->pointL[i].pos[0]) MAX_X = mesh->pointL[i].pos[0];
if (MAX_Y < mesh->pointL[i].pos[1]) MAX_Y = mesh->pointL[i].pos[1];
if (MAX_Z < mesh->pointL[i].pos[2]) MAX_Z = mesh->pointL[i].pos[2];
}

for (int i=0; i< (int) mesh->faces; i++) {
trimesh[no].indices[3*i] = mesh->faceL[i].points[0];
trimesh[no].indices[3*i+1] = mesh->faceL[i].points[1];
trimesh[no].indices[3*i+2] = mesh->faceL[i].points[2];
}
return 1;
}

// トライメッシュデータのサイズ変更
int normalize(int no, Lib3dsMesh *mesh)
{
if (mesh->points == 0) return 0;

for (int i=0; i< (int) mesh->points; i++) {
trimesh[no].vertices[3*i]  *= (dReal) ADJUST;
trimesh[no].vertices[3*i+1] *= (dReal) ADJUST;
trimesh[no].vertices[3*i+2] *= (dReal) ADJUST;
}
return 1;
}

void load3dsModel(const char *filename)
{
int points, faces, no;
Lib3dsMesh *p;

file = lib3ds_file_load(filename);
if (!file) {
puts(“3dsplayer: Error: Loading 3DS file failed.\n”);
exit(1);
}

for (p=file->meshes; p!=0; p=p->next) {
calModel(p, &points, &faces);
if (points == 0) continue;
MeshCount++;
}

trimesh = (MyTrimesh *) malloc(MeshCount * sizeof(MyTrimesh));
no = 0;
for (p=file->meshes; p!=0; p=p->next) {
calModel(p, &points, &faces);
if (points == 0) continue;

vCount[no] = points;
iCount[no] = faces;
trimesh[no].vertices = (float *) malloc(vCount[no] * sizeof(float) * 3);
trimesh[no].indices = (int *) malloc(iCount[no] * sizeof(int) * 3);
trimesh[no].vertexCount = vCount[no];
trimesh[no].indexCount = iCount[no];
no++;
}

no = 0;
for (p=file->meshes; p!=0; p=p->next) {
if (meshDump(no, p)) no++;
}

dReal maxLength;

if (MAX_X – MIN_X >= MAX_Y – MIN_Y) {
if (MAX_X – MIN_X >= MAX_Z – MIN_Z) maxLength = MAX_X – MIN_X;
else maxLength = MAX_Z – MIN_Z;
}
else {
if (MAX_Y – MIN_Y >= MAX_Z – MIN_Z) maxLength = MAX_Y – MIN_Y;
else maxLength = MAX_Z – MIN_Z;
}

ADJUST = BASE_LENGTH / maxLength;

no = 0;
for (p=file->meshes; p!=0; p=p->next) {
if (normalize(no, p)) no++;
}
}
[/code]

[code]
// ode3dsloader.h

#include
#include
// for lib3ds
#include #include #include #include #include #include #include #include #include

void load3dsModel(const char *filename);

[/code]


続く…


20 Comments
  1. koboさん,

    テストしたいので,うさぎの3dsファイルを私に送って頂けませんか?
    メールアドレスはProfileカテゴリにあります.

    でむ

  2. koboさん,

    コメントありがとうございます.
    現在,ロボカップジャパンオープンで取り込んでいます.回答はゴールウィーク明けになると思います.

    すみません.

    でむ

  3. 本を買い、非常に興味深く、勉強させて頂いております。
    3DSのファイルを読みんで表示させようとしておりますが、うまくいきません。
    lib3dsのライブラリを使おうとしています。
    ライブラリの組み込みや、サンプルのプログラムはうまくいったのですが、
    公開されているode3dsloader-0.0.tgzファイルだと、うさぎが読み込めません。
    どこがおかしいのか?教えて頂けないでしょうか??

    症状としては、

    ode3dsloader.cpp の以下の場所で

    84行目
    for (p=file->meshes; p!=0; p=p->next) {
    calModel(p, &points, &faces);
    if (points == 0) continue;
    MeshCount++;
    }

    file->meshesが0になり、MeshCountのカウントアップが0で
    進んでいないようです。

    ファイルの読み込みは、
    puts(“3dsplayer: Error: Loading 3DS file failed.\n”);
    にはならないので、読み込めていると思われます。

    ご指導頂ければ幸いです。

  4. lib3ds-examples.tgzを試してみましたが下記のとおりとなってしまいました。msysのトップからのディレクトリ構成と主要ライブラリのメモ書きがあったらメールに添付していただけますでしょうか?

    $ make
    g++ -Wall -fno-exceptions -fno-rtti -g -DWIN32 -c 3dsplay.c -L/lib -L/usr/lib -L../lib -L../../drawstuff/src -L/usr/local/lib -I/include -I/usr/include -I../include -I. -I../../include -I/usr/local/include
    In file included from ../../include/lib3ds/types.h:51,
    from ../../include/lib3ds/background.h:27,
    from ../../include/lib3ds/file.h:27,
    from 3dsplay.c:27:
    ../include/stdint.h:24:20: stddef.h: No such file or directory
    In file included from ../../include/lib3ds/types.h:78,
    from ../../include/lib3ds/background.h:27,
    from ../../include/lib3ds/file.h:27,
    from 3dsplay.c:27:
    ../include/stdio.h:28:20: stdarg.h: No such file or directory
    In file included from ../../include/lib3ds/types.h:78,
    from ../../include/lib3ds/background.h:27,
    from ../../include/lib3ds/file.h:27,
    from 3dsplay.c:27:
    ../include/stdio.h:191: error: `size_t’ has not been declared
    ../include/stdio.h:191: error: ISO C++ forbids declaration of `parameter’ with no type
    ../include/stdio.h:202: error: `size_t’ has not been declared

  5. ミルトンさん、

    ディレクトリ構成全体とは、msysのトップディレクトリからですか? それでも/home/ユーザ名/src/ode-0.9からでしょうか?

    でむ

  6. ミルトンさん、

    3dsplay.cはそのままではエラーになるので、ソースを変更する必要があります。C言語のソースコードをg++というC++のコンパイラーでコンパイルしているのも原因の一つですが、gccでコンパイルしてもエラーは残ります。

    これは、demura.netのサンプルプログラムをVisual C++でもコンパイルできるようにするためです。C++の方がCより型チェックが厳しいですから。

    なお、コンパイルできるようにしたファイル一式をまとめて以下に置きましたのでゲットしてください。makeしてください。http://demura.net/archives/images/ode/lib3ds-examples.tgz

    でむ

  7. おそれいりますが、ディレクトリ構成全体をワードなどに貼り付けて送っていただけますでしょうか?
    最初から確認しなおします。

  8. ライブラリの位置が原因なのかincludeの位置が悪いのかと思い、makeで指定しているフォルダのファイルを自分のディレクトリにコピーしたりして様子を見ていますが、主だった成果が得られていません。
    自分のディレクトリというのは/msys/1.0/home/username/src/ode-0.9/mytestここです。

    環境自体がめちゃくちゃになっていたと思い、MinGW,Msysをマニュアルで解凍、展開、make installしなおしました。腑に落ちないのが、MinGW,Msysをdownloadするときに同時にダウンロードされる圧縮ファイルをインストールしたほうがよいのかと思い、インストールしましたが内容は変わらないようでした。問題ないとおもいました。

    コメントいただければおねがいします。

  9. 修正したのですが3dsplay.exeはまだできていません。ode3rdloader rabbit.3dsも動きません
    恐れ入りますが、コメントいただきたく。

    $make

    $ g++ -Wall -fno-exceptions -fno-rtti -g -DWIN32 -c 3dsplay.c -L/lib -L/usr/li
    b -L../lib -L../../drawstuff/src -L/usr/local/lib -I/include -I/usr/include -I

    ../include -I. -I../../include -I/usr/local/include
    3dsplay.c: In function `void toggle_bool(int, int, void*)’:
    3dsplay.c:178: error: invalid conversion from `void*’ to `int*’
    3dsplay.c: In function `void render_node(Lib3dsNode*)’:
    3dsplay.c:479: error: invalid conversion from `void*’ to `float (*)[3]’
    3dsplay.c:515: error: invalid conversion from `void*’ to `Player_texture*’
    3dsplay.c:540: warning: unused variable ‘upload_format’
    3dsplay.c: In function `void display()’:
    3dsplay.c:790: error: expected unqualified-id before ‘,’ token
    3dsplay.c:790: error: expected unqualified-id before ‘,’ token
    3dsplay.c:834: error: expected primary-expression before ‘=’ token
    3dsplay.c:835: error: expected primary-expression before ‘=’ token
    3dsplay.c:858: error: expected primary-expression before ‘<=’ token
    3dsplay.c:858: error: expected primary-expression before ‘=’ token
    3dsplay.c:858: error: invalid type argument of `unary *’
    3dsplay.c:860: error: expected primary-expression before ‘,’ token
    3dsplay.c:860: error: expected primary-expression before ‘)’ token
    3dsplay.c: In function `void create_icons()’:

  10. ミルトンさん、

    セパレータエラーはmakefileの中でTABが入っていないからです。
    TABというのはキーボードのTABキーのことです。
    私も初めてこのエラーに出会ったときは意味不明でした。

    このウェブサイトの性質上で、カットアンドペーストでTABをコピーできないのでエラーになります。
    以前のコメントのmakefileでTABをいれる場所に[TAB]と書きましたのでコピーしてからTABを入れてください。

    わからなければ、また質問お願いします。

    でむ

  11. msys/1.0/home/username/src/lib3ds-1.3.0/example/3dsplay.c
    を掲載のmake文で実行しようとmakefileとしてセーブして、msysでmakeしたら
    セパレータエラーが起こってしまいました。全角半角変換は完了しても再び下記の
    メッセージがでました。

    $ make
    makefile:14: *** missing separator. Stop.

  12. ミルトンさん、

    補足します。

    3dsplay.cのコンパイルにはmakefileを使ってください。
    また、3dsplay.cはglutを使っているのでglutをインストールする必要があります。

    glutのインストール法は
    http://www-sens.sys.es.osaka-u.ac.jp/wakate/tutorial/group3/glui/glui3.html
    の記事のWindows XP, MinGWのgccが参考になります。

    # makefile
    CC = g++ -Wall -fno-exceptions -fno-rtti -g -DWIN32
    TARGET = 3dsplay
    OBJS = 3dsplay.o
    SOURCE = 3dsplay.c
    HEADER =
    WINDRES = windres
    LIBS = -L/lib -L/usr/lib -L../lib -L../../drawstuff/src -L/usr/local/lib
    INDS = -I/include -I/usr/include -I../include -I. -I../../include -I/usr/local/include
    OPTS = -lstdc++ -lcomctl32 -lkernel32 -luser32 -lgdi32 -lopengl32 -lglu32 -lglut32 -lwinmm -lm -l3ds

    $(TARGET):$(OBJS) $(RESOURCE_FILE) $(HEADER)
    [TAB] $(CC) -mwindows -o $@ $(OBJS) $(LIBS) $(INDS) $(OPTS)

    $(OBJS): $(SOURCE) $(HEADER)
    [TAB] $(CC) -c $(SOURCE) $(LIBS) $(INDS)

    clean:
    [TAB] rm $(TARGET) $(OBJS) *.*~ *~ *.exe *.*.stackdump

  13. ミルトンさん、

    頂いたrabbit.3DSをode3dsloader-0.0で実行したところ問題なくできました。
    rabbit3DSのパスの指定が悪いと思われます。
    rabbit3DSをmain.cppと同じディレクトリに置いて、main.cppのmakeModelの170行目にある行を以下のように設定して試してください。

    load3dsModel(“./rabbit.3DS”);

    または、lib3dsライブラリが正しくインストールされていない可能性もあります。
    lib3ds-1.3.0/examples/3dsplay.exeがうまく実行できますか?

    でむ

  14. ミルトンさん、

    makeできなかった原因を教えて頂けますか?
    また、表示に使用した3dsファイルをメールに添付して私に送ってください。
    メールアドレスはナビゲーションバーのProfileに記載しています。

    でむ

  15. make は正常に終了しターゲットができました。しかし、肝心のデータを入れても下記のエラーが発生してしまいます。rabbit データはhome/usernale/modelの中にあります。

    home/username/src/ode-0.9/mytest/ode3dsloader-0.0
    $ 3dsloader ../model/rabbit.3DS
    3dsplayer: Error: Loading 3DS file failed.

    ode3dsloaderのソースファイルを分析すればわかると思いますが、ファイルの形式の問題だったらわかりません。申し訳ありませんが、ご指導いただきたく。

  16. ミルトンさん、

    パスが通っていればどこでも良いと思いますが、このODE講座ではc:¥msys¥1.0¥home¥ユーザ名¥src¥ode-0.9としています。

    本講座で使用しているmakefileは場所に依存します。これはdrawstuffのリソースファイルresources.rcを相対パスで指定しているためです。

    お勧めは以下のディレクトリを作り、そこで展開してください。
    /home/ユーザ名/src/ode-0.9/myprog/

    展開すると次のようになります。
    /home/ユーザ名/src/ode-0.9/myprog/ode3dsloader-0.0/{ode3dsloader-0.0.cpp, ode3dsloader-0.0.h, …}

    なお、本講座のサンプルプログラムが問題なくmake並びに実行できる場合は、その問題ないmakefileと入れ替えて、makefileのファイル名を書き直しmakeしてください。

  17. ODE-0.9はどのディレクトリにおくべきでしょうか?
    msysのhome/*****の下でよいのでしょうか? それもーともminGWのしたでしょうか?

  18. ODEをインストールする際にmake install できなく、VisualC++2005で設定しました。他のodeのsanple prpogramを実行して特認台はありませんでした。
    http://demura.net/archives/9ode/lecture/ode22.htmlに従うように、ode/pde.h,drawatuff/drawstuffを移動して、エラーを回避していたら、このエラーに遭遇しました。

    ODEをインストールする際にmake installで、/usr/local/include/odeにodeのヘッダファイル群、/usr/local/libにlibode.a, libode.dllがインストールされていますか?に対しては、ODEはmake Installしていません。したがって、記述のディレクトリにファイルが入っていません。ode-srcからインストールをしたほうがよいようですね。

    また、他のサンプルプログラムはMinGWで問題なくコンパイルできるのでしょうか?わかりません。
    ode-0.9をインストールする際、,sysでインストールはしていません。

  19. ミルトンさん テストして頂きありがとうございます。

    私の環境 MSYS1.0.10+ MinGW5.1.3+Windows Vista Businessでは問題なくmakeできます。 ちなみにmake 3.79.1、gcc3.4.2です。

    ODEをインストールする際にmake installで、/usr/local/include/odeにodeのヘッダファイル群、/usr/local/libにlibode.a, libode.dllがインストールされていますか?

    また、他のサンプルプログラムはMinGWで問題なくコンパイルできるのでしょうか?

  20. ode3dsloader-0.0をmakeするとき、下記のエラーが発生してしまいます。バックスラッシュが/だとよいのですが、どうやって解決したらよいのかご指導いただければと思っています。環境はMinGW+MSYS+WinX PPro SP2
    です。よろしくお願いします。

    c:\MinGW\bin\..\lib\gcc\mingw32\3.4.5\..\..\..\..\mingw32\bin\ld.exe: cannot find -lode

コメントを残す

メールアドレスが公開されることはありません。