mieki256's diary



2024/04/01(月) [n年前の日記]

#1 [prog] 疑似3D道路を描画するスクリーンセーバをC++とOpenGLで書いた

ここ最近ずっと作っていた、C++ と OpenGL を使って疑似3D道路を描画するWindows用のスクリーンセーバを、github にアップロードしておいた。

_mieki256/ssp3droadgl: Screensaver for Windows that draws pseudo 3D roads, implemented in C++ and OpenGL.




実行バイナリファイルは以下の2つ。

残っている問題点 :

このプログラム、メインPC上では何度試してもすんなりヌルヌルと動くのだけど、サブPCでは動く時と動かない時があって、どうもまだちょっとどこかしら怪しい部分が…。ちなみにPCのスペックは以下。

  • メインPC : AMD Ryzen 5 5600X + NVIDIA GeForce GTX 1060 6GB + RAM 16GB。Windows10 x64 22H2。画面解像度 1920x1080。
  • サブPC : AMD Athlon 5350 + 内蔵GPU Radeon R3 + RAM 4GB。Windows10 x64 22H2。画面解像度 1920x1200。

どうしてサブPC上ではすんなり動かないのだろう…。

リモートデスクトップソフトが絡んでないか :

サブPC上では、メインPCからLAN経由で操作できるように、リモートデスクトップソフト Brynhildr をサーバモードで常駐させていた。もしかしたらそのせいでOpenGLを使ったプログラムが、ちと動かしづらい状態になっていたのかもしれない。

管理者権限で brynhildr.exe を実行して、Service で「Delete」を選ぶことでサーバモードのサービスを無効にしてから Windows10 を再起動したら、今回作成したスクリーンセーバが起動できる確率がグンと上がったような気がする。

ただ、完全に問題が解消されたわけでもなく。スクリーンセーバが呼び出された際、最初の1〜2回の呼び出し時は何故か起動に失敗する…。3回目ぐらいでスクリーンセーバが起動するようになって、一度起動してしまえば、そこから先は何度も起動が成功するし、ずっと動き続けてくれるのだけど…。

また、スクリーンセーバ版ではなく、GLFWを使った版は、Brynhildr がサーバモードで動いていても問題無く起動するし、描画もしてくれる…。

やはりスクリーンセーバとして動かそうとした時だけ、何か妙なことが起きてるとしか思えない。でも、メインPC上なら毎回必ず起動してくれるのだよな…。何故サブPC上ではダメなのか…。

テクスチャが大き過ぎるのだろうか :

今回、ビルボード用テクスチャが 4096 x 4096 x 1枚、BG用テクスチャが 2048 x 1024 x 4枚と、結構巨大なサイズのテクスチャになってるあたりも不安要素。png や jpg をベタデータに展開する際に時間がかかってしまっているだろうし…。何より、そんなサイズでVRAMを占有してしまって大丈夫なのかどうか…。そのせいで動作が怪しくなっている可能性は…。

いや、それは違う気もする。もしテクスチャのサイズが巨大過ぎて問題が起きてるなら、GLFW版の動作も同様に怪しくならないとおかしい。しかしGLFW版はすんなり何度も確実に動いてしまっているわけだから…。テクスチャサイズはこれでも問題無いということなんだろう…。

余談。ビルボードについての課題 :

今回、各ビルボードを、四角ポリゴン(GL_QUADS)でドーンと表示してるけど。コレをちゃんと作るなら、複数のポリゴンを組み合わせて一つのビルボードにしたほうがいいのかもしれない。大昔の2Dゲームだって、スプライトを複数枚組み合わせて1つのキャラを作る場面があったわけだし…。

例えば…。
  • 横方向にずらりと並ぶ花畑的なビルボードを描画したいなら、横方向に異様に長いテクスチャを用意するより、小さい正方形のスプライトを横にいくつも並べたほうがテクスチャ領域が節約できる。
  • 木のビルボードも、葉っぱと幹を分けて組み合わせて作れば、幹の脇の透過部分を描画しなくて済む。
  • 道路の上をまたぐ橋っぽいビルボードも、柱部分と横に長い部分で分けて作れば、真ん中の透過部分を描画しなくて済む。

しかし、そのあたりを考えていくと、いっそ3Dモデルを作って描画したほうがいいのでは…。どうせ今回OpenGLを使って描画しちゃってるわけだし。地面と道路は疑似3Dだけど、その上に載せる各オブジェクトが3Dモデルになっていても何ら問題は無いよな…。もしかすると、いかにもな3Dモデルじゃなく、Z軸方向に差がある状態で複数枚のスプライト相当を重ねてあるだけでも、視差効果/パララックスが得られるかもしれない。

もっとも、3Dモデルにしてしまうのはやり過ぎかも。元々、レトロな見た目が欲しくてわざわざ疑似3Dにしているのに、3Dに寄せ過ぎるのもどうなんだろう。だったら道路も含めて全部3Dにしてしまえばいいじゃん、という話にもなりそう。

まあ、OpenGLでモデルデータを描画するのも結構一苦労しそうなので、今回はこのままでいいかなと…。

2024/04/02(火) [n年前の日記]

#1 [prog] 自作スクリーンセーバが起動しにくい問題について試行錯誤中

_昨日githubにアップロード した、C++ (MinGW g++ 6.3.0) と OpenGL で自作したWindows用スクリーンセーバが、サブPC上で起動しにくい問題について、解決策がないか試しているところ。

状況は以下。
つまり、スクリーンセーバとして作ると、サブPC上で利用しようとした時だけ、動いたり動かなかったりする状態になってしまう。

初期化処理は最低限の処理だけにしておくと良さそう :

スクリーンセーバを作る際の前提として、以下があるわけだけど…。
  • C/C++ (MinGW gcc/g++ 6.3.0) でスクリーンセーバを作る際、scrnsaveライブラリを使うと作りやすくなる。scrnsave.h を include して、libscrnsave.a をリンクする。
  • scrnsaveライブラリを使えば、たった3つの関数 ―― ScreenSaverProc()、ScreenSaverConfigureDialog(), RegisterDialogClasses() を自分で書くだけでスクリーンセーバが作れる。
  • ScreenSaverProc() 内で、WM_CREATEメッセージが送られてきたら初期化処理を行い、SetTimer() で一定時間毎に処理が呼ばれるように ―― 指定時間毎に WM_TIMER が送られてくるように指定する。
  • ScreenSaverProc() 内で、WM_TIMERメッセージが送られてきたら、毎フレームの処理を行う。

ふと、WM_CREATE が飛んできた際、そこで時間がかかる初期化処理をしてしまうとよろしくないのではないか…? と思えてきた。昨日の段階では、WM_CREATE が来た時に画像の展開処理もしていて、そこで数秒待たされる状態になっていた。

試しに、WM_CREATE が来た時は最低限の初期化処理だけをして、画像の展開は各フレーム処理内で行うように変更してみた。更に、展開すべき画像5枚分を一気に展開せず、1フレームにつき画像を1枚ずつ展開して、5フレームかけて全画像を展開するようにした。

このように変更したところ、サブPC上でも、スクリーンセーバが起動してくれる確率がグンと上がった。
  • リモートデスクトップソフト Brynhildr をサーバモードで常駐させていても、起動する確率は高い。
  • 1920x1200の画面でも、全てが問題無く描画できるようになった。
今までは、Brynhildrを落としておくとか、デスクトップ解像度を下げるとか、BG画像の4枚分は展開しない/BG描画はしないようにしないと動いてくれなかったりしたので、状況はかなり改善された。

残っている問題 :

ただ、これでもまだ問題が残ってる。
  • 最初の呼び出し時は、一瞬画面がチラついて、すぐにスクリーンセーバが終了してしまう。
  • そのまま放置して2度目の呼び出しが来ると、今度はちゃんとスクリーンセーバが起動してくれる。
何度試しても、こういう状態になる…。何が原因なのか。解決策/回避策は無いのだろうか。

メインPC上ではこんな問題は起きてない。サブPC上でだけ起きてるのだよな…。AMD製GPU(Radeon R3)のせい…?

2024/04/03追記 :

原因が分かった。デスクトップ解像度が1920x1200になっているとこの不具合が発生する。1920x1080にすれば一発でスクリーンセーバが起動する。そんなオチかよ…。トホホ。

2024/04/03(水) [n年前の日記]

#1 [prog][windows] Windows用の自作スクリーンセーバが動かない問題の回避策が分かった

_昨日、 C++ (MinGW g++ 6.3.0)とOpenGLで自作したWindows用スクリーンセーバが、サブPC上で起動したりしなかったりする問題で悩んでいた。症状は以下。
サブPCのスペックは以下。
原因が分かった。デスクトップ解像度だった。

サブPC上で選択できる最大解像度、1920x1200にしていると、この問題が起きてしまう。しかし、Windowsのデスクトップ解像度設定画面で「(推奨)」と表示されている1920x1080にすると、何の問題もなく一発でスクリーンセーバが起動するようになった。

そこかよ…。スクリーンセーバの作り方がまずかったわけではないのだな…。

このあたり、サブPCの内蔵GPU (Radeon R3, GCN世代)のスペックに絡む問題なのか、それとも、HDMI接続している液晶ディスプレイ MITSUBISHI MDT243WG-SB の問題なのか、一体どのへんが絡んでるのか分からないけれど、どうもこのサブPCは、デスクトップ解像度を1920x1200にして利用しようとすると、こういったところで妙な問題が発生するようだなと…。それともまさか、使っているHDMIケーブルのスペックの問題なのだろうか…?

何にせよ、なんだか動作が変だなと思ったら、「(推奨)」と表示されてるデスクトップ解像度に変えてみるのもアリ。なのかもしれない。

なんだかこの件はそのうち忘れそう。覚えていられる自信がない…。

OpenGLを使わなければ問題無い :

試行錯誤した際に一応分かった点をメモしておく。

まず、OpenGLを使ってないスクリーンセーバなら、解像度が1920x1200でも、こういった問題は起きない。

例えば、以下で、OpenGLを使っていないスクリーンセーバのサンプルコードが紹介されているけれど、これをビルドして、スクリーンセーバ(*.scr)にして試した際は、解像度が1920x1200でも、何の問題もなく一発ですんなりと起動することが分かった。

_Screen Saver 入門 (WebArchive)


また、以下で紹介されてるサンプルコード、もしくは自分が書いたコードは、OpenGLを使っているスクリーンセーバだけど。1920x1200では、例の不具合が ―― 1回目の呼び出しですぐ終了して、2回目の呼び出しで起動成功する不具合が発生した。そして1920x1080なら、そういった問題は発生しない。どのスクリーンセーバも一発でちゃんと起動した。

_Windows - OpenGLスクリーンセーバーサンプル: インディーズゲームデベロッパー「OMEGA POINT」
_How to Scr: Writing an OpenGL Screensaver for Windows
_mieki256/glboundballscr: Bound ball animation screensaver win32 by using OpenGL.


更に、以下はソースコードは公開されてなくて実行バイナリしかないスクリーンセーバだけど、これもOpenGLを使っているので、1920x1200では件の不具合が発生して、1920x1080なら一発で起動した。

_TeapotGLスクリーンセーバーの詳細情報 : Vector ソフトを探す!


そんなわけで、「OpenGLを使っていて」、かつ、「(推奨)」と表示されていないデスクトップ解像度にしている場合、件の不具合が発生する可能性が出てくるらしい。

スクリーンセーバじゃなければ問題無い :

念のために一応書いておくけれど、スクリーンセーバではなく、GLFWで新規にウインドウを作成してOpenGLで描画する分には、デスクトップ解像度が1920x1200でも問題無く動作した。

つまり、OpenGLの勉強をするだけなら、この問題には遭遇しない予感。フツーは GLUT や GLFW を使って実験するだろうから。

「せっかくここまで作ったんだから、試しにスクリーンセーバにでもしてみようかな」
「選択できる最大解像度で動作確認しておかないとマズイだろう」

などと欲を出して(?)作業をし始めると、こうしてハマる。でもまあ、Windows上で「(推奨)」と表示されてるデスクトップ解像度で動かしてる分には、この問題には遭遇しないだろうけど…。たぶん。

#2 [pc] サブPCを操作するのが面倒臭い

サブPCを操作して各種実験をする際、なんだかちょっと面倒臭い。サブPCには無線接続のキーボードを繋いであるので、目の前にはメインPC用のキーボード(USB有線接続)とサブPC用のキーボード(無線接続、タッチパッド付)の2つがある状態で…。サブPC用のキーボードを膝の上に置きながら作業してるけど、フラフラして打ちづらい。タッチパッドの操作もしづらい。

このへんどうにかならんかなあ…。とりあえず思いついた案をメモして考えをまとめてみよう…。。

キーボードを宙に浮かせられないか :

膝の上にあるキーボードが鬱陶しい。このキーボードを宙に浮かせられないか。ディスプレイアーム/モニターアームにノートPCを乗せられるようにする製品があった気がする。ソレをPCデスクに固定して使えば、キーボードを宙に浮かせられるよな…。

でも、ディスプレイと違って、キーボードは常に打鍵して力を加えるものだから、その手のアームで浮かせたらフラフラして使えたもんじゃないかもしれない。うっかり力を入れ過ぎてアームが外れたら危険だよな…。

サイドテーブルはどうか :

キーボードが置ける程度の小さいサイズのサイドテーブルのようなものはないか。と思ってググってみたけど…。そこまで小さいサイズのサイドテーブルは見当たらなかった。

そこそこ大きいサイズなら、いくらでも見かけるけれど、それは自分も持ってるのだよな…。ずっと畳んだ状態で部屋の隅で埃を被っているけれど。引っ張り出すのが面倒臭い。ここまで大きくなくてもいいんだよな…。

キーボードとマウスを1つずつにできないか :

そもそもキーボードとマウスが2つあるのがおかしい。人間の手の本数は限られているのだから、瞬間的には1つのキーボード、1つのマウスしか操作できない。だったら、2つは要らない。本来は1つで十分なはず。

となると、CPU切替器かな…。1つのキーボード、1つのマウスの先にCPU切替器があって、そのCPU切替器から2台のPCにUSBケーブルが伸びていて…。どう考えてもケーブルが邪魔になりそう。サブPCはメインPCから結構離れたところに置いてあるし。

Bluetoothはどうか :

キーボードとマウスを1つずつにするなら別の方法もあるなと。Bluetooth接続のキーボードとマウスを使えば、接続先を切り替えて複数のPCに対応できるのではないか。その手のキーボードは、3台ぐらいまで登録できそうでもあるし。Bluetoothだからケーブルも無いし。

各PC用にBluetooth受信機を用意しないといけない…。そもそも新規にBluetoothキーボードとマウスを購入しないと…。自分が気に入りそうなキーボードって無さそうな気もする…。

Bluetooth接続キーボードは、BIOS設定の操作ができないのもアレだな…。もっとも、BIOS設定ってそう頻繁に変更するものでもないから、そういう作業をする時だけ有線接続のキーボードを繋いでやれば済む話だろうか。

LAN接続してリモート操作でいいのでは :

どのサブPCもLAN接続しているのだから、LAN経由でリモート操作すればいいのではないか。実際、Linux機に対してはメインPCから ssh でアクセスして操作することが多いのだし。VNCを使ってデスクトップ画面を操作することもあるし。LAN経由で操作する分にはキーボードもマウスも1つで済む。新規に何か買わなくてもいい。

ただ、リモート操作は反応が遅くて…。CUIで操作する分には問題無いけど、VNCで操作すると画面がベロンベロンと書き換えられる様子が見えてしまって…。各サブPCは無線LANでLANに接続しているから速度が出なくてベロンベロン状態になるんだろうけど。いっそ有線で接続してしまおうか…。

日常的に使うわけでもないからいいか :

「BIOS設定は頻繁に変更するものではない」と書いたところで気が付いた。そもそも、サブPCで各種実験をする機会自体がそんなにないのだよな…。サブPCは常に電源を入れてるわけでもない。日常的に、頻繁に使ってるわけではなく、たまにしか使わない…。

だったら別に、このままでもいいんじゃないのか…。年に何回あるのか分からない場面のために、何かしらを新規購入するのもコスパが悪過ぎるだろう…。今の状態で我慢しよう…。

2024/04/04(木) [n年前の日記]

#1 [prog] 自作スクリーンセーバをLinuxに対応させたい

C++とOpenGLを使ってWindows用のスクリーンセーバを自作したけれど。せっかく作ったのだから Linux上でもビルドできるようにしたい。もっとも、Linux用のスクリーンセーバを作るわけではなくて、せめてGLFWを使ったデモプログラムとして動かせるようにするつもり。

Ubuntu Linux 20.04 LTS上でビルドしようとしているけれど、いくつか問題に遭遇。

Linux には tchar.h は無いのだな…。とりあえず今回は、#include <tchar.h> をコメントアウトするだけでビルドできそう。

MessageBox() も無いな…。コレもとりあえずコメントアウトしておく方向で。

実行バイナリに埋め込むバイナリファイル(*.png, *.jpg)のシンボルも、ちょっと変わる。MinGW g++ 6.3.0 用は先頭に「_」がつかないけれど、MSYS2 g++ 13.2.0 や Ubuntu Linux 22.04 LTS の g++ 11.4.0 なら先頭に「_」がつく。

ソースの切り替えについて :

Windows用とLinux用の両方でビルドが通るように、定義済みマクロでソースの中身が変わるようにした。
  • Windows上でビルドする際は _WIN32 が定義される。
  • MSYS2上でビルドする際は __MINGW64__ が定義される。
  • Linux上でビルドする際は __linux__ が定義される。

#if defined(__MINGW64__) || defined(__linux__)

// MSYS2 (Windows, g++ 13.2.0) or Linux

#else

// MinGW (Windows, g++ 6.3.0)

#endif


#ifdef _WIN32

// Windows only

#include <tchar.h>
#include <windows.h>
#include <scrnsave.h>
#include <mmsystem.h>

#endif

Ubuntuのバージョンによって結果が違ってしまう :

VMware Player + Ubuntu Linux 20.04 LTS x64 ならビルドが通るのだけど。実機 + Ubuntu Linux 22.04 LTS x64 ではビルドが通らない…。

Ubuntu Linux 20.04 LTS x64 の場合。ビルドは通る。
$ g++ --version
 g++ (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0
 ...

$ ld --version
 GNU ld (GNU Binutils for Ubuntu) 2.34
 ...

$ make -f Makefile.glfw
g++ -o ssp3droadglfw.o -c ssp3droadglfw.cpp
g++ -o render.o -c render.cpp
ld -r -b binary -o sprites.o sprites.png
ld -r -b binary -o bg_summer.o bg_summer.jpg
ld -r -b binary -o bg_autumn.o bg_autumn.jpg
ld -r -b binary -o bg_winter.o bg_winter.jpg
ld -r -b binary -o bg_night.o bg_night.jpg
g++ -o ssp3droadglfw ssp3droadglfw.o render.o sprites.o bg_summer.o bg_autumn.o bg_winter.o bg_night.o -lGL -lGLU -lglfw -lm


Ubuntu Linux 22.04 LTS x64 の場合。エラーが出てしまう…。
$ g++ --version
 g++ (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
 ...

$ ld --version
 GNU ld (GNU Binutils for Ubuntu) 2.38
 ...

$ make -f Makefile.glfw
g++ -o ssp3droadglfw.o -c ssp3droadglfw.cpp
g++ -o render.o -c render.cpp
ld -r -b binary -o sprites.o sprites.png
ld -r -b binary -o bg_summer.o bg_summer.jpg
ld -r -b binary -o bg_autumn.o bg_autumn.jpg
ld -r -b binary -o bg_winter.o bg_winter.jpg
ld -r -b binary -o bg_night.o bg_night.jpg
g++ -o ssp3droadglfw ssp3droadglfw.o render.o sprites.o bg_summer.o bg_autumn.o bg_winter.o bg_night.o -lGL -lGLU -lglfw -lm
/usr/bin/ld: render.o: relocation R_X86_64_PC32 against absolute symbol `_binary_sprites_png_size' in section `.text' is disallowed
collect2: error: ld returned 1 exit status
make: *** [Makefile.glfw:26: ssp3droadglfw] エラー 1

このエラーはどういう意味なんだろう…。

テクスチャを展開できない :

ビルドが通る、VMware Player + Ubuntu Linux 20.04 LTS x64上でも、動作に問題がある。出来上がった実行バイナリを動かしてみると、テクスチャ画像を展開できる時と、展開できない時がある…。必ず展開できるわけでもなく、必ず展開できないわけでもない。こういうバグが一番困る…。

実機上なら動作が変わるのだろうか。しかし、そちらは Ubuntu Linux 22.04 LTS だから、ビルド自体が通らない…。

#2 [prog] MSYS2パッケージをダウングレードしておいた

ここ最近、xzパッケージにバックドアが仕掛けられていたという事件が気になってる。MSYS2 のxzパッケージを調べてみたら、事件を起こしたバージョン、5.6.1 が入ってた…。

pacman -Ss xz

念のために、一つ前の 5.4.6-2 にダウングレードしてみたい。MSYS2上でそんなことをして意味があるのかないのか分からんけど…。まあ、pacman でダウングレードする方法を知っておいてもいいだろう…。

_パッケージのダウングレード - ArchWiki
_MSYS2でパッケージをダウングレードする - Qiita

/var/cache/pacman/pkg/ に、pacmanのキャッシュが残ってるらしい。調べてみたら、古いバージョンも残っていた。

$ ls /var/cache/pacman/pkg/ | grep xz
mingw-w64-i686-xz-5.4.5-1-any.pkg.tar.zst
mingw-w64-i686-xz-5.4.5-1-any.pkg.tar.zst.sig
mingw-w64-i686-xz-5.4.6-1-any.pkg.tar.zst
mingw-w64-i686-xz-5.4.6-1-any.pkg.tar.zst.sig
mingw-w64-i686-xz-5.4.6-2-any.pkg.tar.zst
mingw-w64-i686-xz-5.4.6-2-any.pkg.tar.zst.sig
mingw-w64-i686-xz-5.6.1-2-any.pkg.tar.zst
mingw-w64-i686-xz-5.6.1-2-any.pkg.tar.zst.sig
mingw-w64-x86_64-xz-5.4.5-1-any.pkg.tar.zst
mingw-w64-x86_64-xz-5.4.5-1-any.pkg.tar.zst.sig
mingw-w64-x86_64-xz-5.4.6-1-any.pkg.tar.zst
mingw-w64-x86_64-xz-5.4.6-1-any.pkg.tar.zst.sig
mingw-w64-x86_64-xz-5.4.6-2-any.pkg.tar.zst
mingw-w64-x86_64-xz-5.4.6-2-any.pkg.tar.zst.sig
mingw-w64-x86_64-xz-5.6.1-2-any.pkg.tar.zst
mingw-w64-x86_64-xz-5.6.1-2-any.pkg.tar.zst.sig
xz-5.4.5-1-x86_64.pkg.tar.zst
xz-5.4.5-1-x86_64.pkg.tar.zst.sig
xz-5.4.6-1-x86_64.pkg.tar.zst
xz-5.4.6-1-x86_64.pkg.tar.zst.sig
xz-5.4.6-2-x86_64.pkg.tar.zst
xz-5.4.6-2-x86_64.pkg.tar.zst.sig
xz-5.6.1-2-x86_64.pkg.tar.zst
xz-5.6.1-2-x86_64.pkg.tar.zst.sig

$ ls /var/cache/pacman/pkg/ | grep lzma
liblzma-5.4.5-1-x86_64.pkg.tar.zst
liblzma-5.4.5-1-x86_64.pkg.tar.zst.sig
liblzma-5.4.6-1-x86_64.pkg.tar.zst
liblzma-5.4.6-1-x86_64.pkg.tar.zst.sig
liblzma-5.4.6-2-x86_64.pkg.tar.zst
liblzma-5.4.6-2-x86_64.pkg.tar.zst.sig
liblzma-5.6.1-2-x86_64.pkg.tar.zst
liblzma-5.6.1-2-x86_64.pkg.tar.zst.sig

pacman -U でダウングレードできる。

pacman -U file:///var/cache/pacman/pkg/mingw-w64-i686-xz-5.4.6-2-any.pkg.tar.zst
pacman -U file:///var/cache/pacman/pkg/mingw-w64-x86_64-xz-5.4.6-2-any.pkg.tar.zst
pacman -U file:///var/cache/pacman/pkg/xz-5.4.6-2-x86_64.pkg.tar.zst
pacman -U file:///var/cache/pacman/pkg/liblzma-5.4.6-2-x86_64.pkg.tar.zst

$ pacman -Ss xz
clangarm64/mingw-w64-clang-aarch64-xz 5.6.1-2
    Library and command line tools for XZ and LZMA compressed files (mingw-w64)
mingw32/mingw-w64-i686-xz 5.6.1-2 [インストール済み: 5.4.6-2]
    Library and command line tools for XZ and LZMA compressed files (mingw-w64)
mingw64/mingw-w64-x86_64-xz 5.6.1-2 [インストール済み: 5.4.6-2]
    Library and command line tools for XZ and LZMA compressed files (mingw-w64)
ucrt64/mingw-w64-ucrt-x86_64-xz 5.6.1-2
    Library and command line tools for XZ and LZMA compressed files (mingw-w64)
clang32/mingw-w64-clang-i686-xz 5.6.1-2
    Library and command line tools for XZ and LZMA compressed files (mingw-w64)
clang64/mingw-w64-clang-x86_64-xz 5.6.1-2
    Library and command line tools for XZ and LZMA compressed files (mingw-w64)
msys/liblzma 5.6.1-2 (libraries) [インストール済み: 5.4.6-2]
    Library for XZ and LZMA compressed files
msys/xz 5.6.1-2 (compression) [インストール済み: 5.4.6-2]
    Library and command line tools for XZ and LZMA compressed files

インストール済み、が 5.4.6-2 になった。

ダウングレードできたら、/etc/pacman.conf の IgnorePkg= にパッケージ名を追加する必要があるらしい。これをしておかないとアップグレードされてしまうのだとか。

vi /etc/pacman.conf
IgnorePkg   = xz liblzma mingw-w64-i686-xz mingw-w64-x86_64-xz

これで合ってるのかな…。分からん…。

#3 [anime] 「勇気爆発バーンブレイバーン」最終回を視聴

録画してたソレをようやく視聴。ロボットアニメ…だろうか? だよな。たぶん。

いやはやコレは…。なんというか…。途中まで「自分は一体何を見せられているんだろう…?」と…。素晴らしい。

なんとなく、「真面目に不真面目」とか「ふざける時は全力で」とかそういう言葉が脳裏に浮かんだ。どうせやるなら徹底的に、だよな…。何にせよ、とんでもないアニメを見せてもらった気がする…。

2024/04/05(金) [n年前の日記]

#1 [prog] 自作スクリーンセーバをLinuxに対応させたい。その2

C++とOpenGLを使った自作スクリーンセーバをLinuxにも対応させようとしているところ。GLFWを使ったデモプログラムとして動かすつもり。

_mieki256/ssp3droadgl

Ubuntu Linux 22.04 LTS (g++ 11.4.0, ld 2.38)上でビルドしようとしたらリンクエラーが出てしまって悩んでしまったけれど。ソースコードに手を入れたら改善した。実行バイナリに埋め込んだpngやjpgを展開させる際、サイズの求め方を変えてみたらエラーが出ない状態になった。

ざっくり説明。

画像バイナリ(.png .jpg)を実行バイナリに含める方法は色々あるけど、今回は ld を使ってバイナリファイルをオブジェクトファイルに変換する方法を選んだ。
$ ld -r -b binary -o sprites.o sprites.png

こうしてできたオブジェクトファイルをリンクしてやることで、実行バイナリの中に画像バイナリを含めることができる。

バイナリファイルを ld でオブジェクトファイルに変換すると、*_start, *_end, *_size の3つのシンボルを、C/C++側から参照できる状態になる。

以下は objdump -x を使って、オブジェクトファイルに含まれている情報をダンプしてみた例。「SYMBOL TABLE:」で、3つのシンボルが ―― *_end、*_size、*_start が並んでることが分かる。
$ ld -r -b binary -o sprites.o sprites.png

$ objdump -x sprites.o
 
 sprites.o:     ファイル形式 elf64-x86-64
 sprites.o
 アーキテクチャ: i386:x86-64, フラグ 0x00000010:
 HAS_SYMS
 開始アドレス 0x0000000000000000
 
 セクション:
 Idx Name          Size      VMA               LMA               File off  Algn
   0 .data         00875cae  0000000000000000  0000000000000000  00000040  2**0
                   CONTENTS, ALLOC, LOAD, DATA
 SYMBOL TABLE:
 0000000000000000 l    d  .data  0000000000000000 .data
 0000000000875cae g       .data  0000000000000000 _binary_sprites_png_end
 0000000000875cae g       *ABS*  0000000000000000 _binary_sprites_png_size
 0000000000000000 g       .data  0000000000000000 _binary_sprites_png_start


C/C++側からこれらのシンボルを利用したい時は、以下のように書いておく。これで各シンボルが参照できるようになる。
extern unsigned char _binary_sprites_png_start; // binary start address
extern unsigned char _binary_sprites_png_end;   // binary end address
extern unsigned char _binary_sprites_png_size;  // binary size


このシンボルを利用して、実行バイナリ内の画像バイナリの展開処理を行うわけだけど…。今までは *_size を使って処理してた(つもりだった)。
        img_ptr = &_binary_sprites_png_start; // start address
        img_size = (size_t)&_binary_sprites_png_size; // size
        gw.spr_tex = createTextureFromMemory(img_ptr, img_size);

これを、end - start でサイズを求めるように修正してみた。
        img_ptr = &_binary_sprites_png_start; // start address
        img_size = (size_t)((&_binary_sprites_png_end) - (&_binary_sprites_png_start)); // size
        gw.spr_tex = createTextureFromMemory(img_ptr, img_size);

このように書き換えてみたところ、エラーが出なくなってビルドが通るようになった。実行バイナリも期待した通りに動作してくれた。

また、この書き換えをしたことで、Ubuntu Linux 20.04 LTS上で発生していた不具合も ―― テクスチャの展開が時々失敗する問題も何故か解決してしまった。何度試しても、必ず展開できる…。もしかして今までは、サイズが不定値になっていたのだろうか…?

#2 [prog] 自作スクリーンセーバを高DPIに対応させる

今時のWindowsは、デスクトップ設定で、文字表示その他を、125%とか150%とかに変更できる。おそらく高DPIと呼ばれる状態なのだろうけど…。

この状態にすると、スクリーンセーバ設定ダイアログ上で、スクリーンセーバのプレビュー表示がおかしな位置に表示されてしまう。この問題を解決したい。

結論を先に書いておく。マニフェストファイルを作成して、スクリーンセーバのリソースファイル内でマニフェストファイルを指定して埋め込んでおけば、一応は正常な表示になる。

環境は、MinGW g++ 6.3.0、ld 2.28、windres 2.28。Windows10 x64 22H2上で作業。

不具合を発見した経緯 :

自作したWindows用スクリーンセーバを ―― 疑似3D道路を描画するスクリーンセーバを、親父さんのPC上で動かして動作確認してみたところ、スクリーンセーバ設定ダイアログ上のプレビュー表示がおかしくなってることに気づいた。本来表示されるべき位置からかなり左上のほうに表示されてしまって、描画内容の右下だけが、プレビュー画面の左上のほうにちょっとだけ表示されてしまっている。何故だ。どうしてこうなった。

そこでふと気が付いた。親父さんは視力が衰えてしまっているので、Windowsのディスプレイ設定で、「テキスト、アプリ、その他の項目のサイズを変更する」を150%にしていた…。たぶんそのせいじゃないだろうか。

自分のメインPC上でも、そのあたりを変更してみたところ、親父さん用PCと全く同じ不具合が再現できた。

今までビルドしてきた各スクリーンセーバについても一通り試してみたところ、OpenGLを使っている/使ってないに係わらず、どのスクリーンセーバも、プレビュー表示は表示位置がおかしくなった。

この結果は、当然な気もする。自分がググって今まで参考にしてきた、スクリーンセーバの作成方法を解説しているページは、「高DPI? ナニソレ?」「WindowsのDPIと言えば96DPIに固定されてるもんだろJK」という時代に書かれているので、こういった不具合が起きることも、もちろん解決策についても、一切言及していない。故に、どのサンプルを動かしてもこういう不具合が発生するのは必然、ということなのだろう…。

マニフェストファイルがあれば動作が変わる :

ただ、試しているうちに、FreeBASICで作成したスクリーンセーバだけが、高DPIにしても正常なプレビュー表示になっていることに気づいた。

_freeBASIC Screensaver Kit Updated - freebasic.net
_FreeBASIC Screensaver Kit - freebasic.net

一体何が違うのか調べてみたところ、FreeBASICのスクリーンセーバ作成用kitには、マニフェストファイル(manifest)が添付されていることに気づいた。

そのマニフェストファイルに書かれていた内容は以下。
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
  <asmv3:application>
    <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
      <dpiAware>true</dpiAware>
    </asmv3:windowsSettings>
  </asmv3:application>
</assembly>

内容についてググった感じでは…。この記述は、Windows Vistaの時点で追加されたDPI変更機能に対応させるための記述、ということらしい。

ちなみに今現在は、Windows10 Version 1607で追加された機能もあるらしくて、完全に対応させるならそのあたりもアプリ側で行わないといかんらしい…。

更に、このマニフェストファイルは、リソースファイルの中で取り込むように指定されていた。リソースファイルの内容は以下。
 FB_SCRNSAVER_ABOUT DIALOGEX 6, 18, 160, 62
 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | DS_NOFAILCREATE
 FONT 10,"Arial"
 CAPTION "Mambazo Doodle"
 BEGIN
     CTEXT "Mambazo Doodle" -1, 0, 5, 160, 8
     CTEXT "Version 1.0" -1, 0, 15, 160, 8
     CTEXT "http://langfordtavern.com/" -1, 0, 25, 160, 8
     DEFPUSHBUTTON "OK" IDOK, 55, 40, 50, 14
 END 
 
 1 24 "MambazoDoodle.xml"

一番最後に、「1 24 "MambazoDoodle.xml"」 という記述がある。
  • ここで指定されているxmlファイルがマニフェストファイル。
  • 最初のほうにある「1 24」は、マニフェストファイルをリソースファイル内で指定する際のお約束らしい。

自作スクリーンセーバにも導入してみた :

試しに、自作のスクリーンセーバでも同じことをやってみた。
  • マニフェストファイル名を、「スクリーンセーバファイル名.manifest」にする。
  • リソースファイル(resource.rc)の最後に、該当行を追加。

1 24 "ssp3droadgl.scr.manifest"

この状態でビルドし直したところ、WindowsのデスクトップのDPIを変更しても、スクリーンセーバ設定ダイアログ上で正常な位置にプレビューが表示される状態になった。

_mieki256/ssp3droadgl

とりあえず今回は、この対応だけでいいんじゃないかな…。

他のスクリーンセーバも対応させておいた :

今まで作った、OpenGLを使った他のスクリーンセーバも対応させておいたとメモ。

_mieki256/ssstars_opengl
_mieki256/glboundballscr

#3 [linux] CUIファイラーRangerでファイルのアクセス権を変更したい

Linux上で動作するCUIファイラー Ranger を使って、複数のファイルのアクセス権を変更したい。今までは bash上で chmod 666 hoge.txt とか打ってたけど、何度もやってると面倒臭い。

以下の手順で変更できた。
あるいは、変更したいファイルにカーソルを合わせて、いきなり「666=」と打ってもいいらしい。見た目で「変更したよ」とは言ってこないけど、実はちゃんと変更されてる。数値を打った後、一番最後に「=」を打つことで、そのアクセス権に変更せよ、という指示を出しているらしい。

参考ページ :


2024/04/06() [n年前の日記]

#1 [linux] rangerで画像表示できるか試してた

自分は普段、Windows10 から Ubuntu Linux 22.04 LTS (Intel Core i3-6100T機) を操作したい際、MobaXterm + ssh でログインして作業している。その際、ファイルの在処などを調べたりする時は、CUIファイラー ranger を使ってる。

_CLI で Linux ファイルマネージャ ranger を使うことのメモ | Jenemal Notes
_CLIファイルマネージャrangerの基本設定 #Python - Qiita
_Ranger: CUI ファイラー - 苦労する遊び人の玩具箱 1 ドキュメント
_ターミナルで利用するCLIファイルマネージャ(ファイラー)の比較・おすすめ [インフラエンジニアのPC環境]

sudo apt install ranger

この ranger は画像のプレビュー表示もできるのだけど、MobaXterm上では画像が表示できなくてエラーになってしまう。どうにかして表示する方法はないかと試してた。

ターミナルエミュレータを起動して解決する :

MobaXterm にはXサーバも同梱されているので、アクセス先の Ubuntu Linux にインストールされているGUIアプリも起動することができる。ターミナルエミュレータ(ターミナルソフト)も起動できるので、それらを起動して、その上で ranger を使えば画像のプレビュー表示もできるのではないかと思いついた。

ただ、それぞれを起動して試してみたところ、urxvt 以外は全滅だった。

sudo apt install rxvt-unicode
urxvt &

ranger_rxvt_ss.jpg

sakura terminal も、roxtem も、rangerの画像表示はできなかった。自分の環境の場合、rangerで画像を表示したい時は urxvt を起動するしかないようだなと…。

一応、 _~/.Xdefaults で urxvt 用の設定はしておいた。設定内容については以下が参考になった。ありがたや。

_urxvt こと rxvt-unicode を使うことのメモ | Jenemal Notes
_Ubuntuにurxvt(軽量ターミナル)を導入 - mfumiの日記

ちなみに、昔の Ubuntu Linux では、この rxvt-unicode は256色に非対応だったらしいけど。説明文を見る限り、Ubuntu Linux 22.04 LTS でインストールできる rxvt-unicode は、256色をサポートしてる、と表示されている。本当に対応してるのかどうかは分からんけど…。
$ sudo aptitude search rxvt
i   rxvt-unicode   - RXVT-like terminal emulator with Unicode and 256-color support

_Ubuntu - jammy の rxvt-unicode パッケージに関する詳細

別の方法で画像をプレビュー表示する :

画像のプレビュー表示をしたいだけなら、GUIのファイラーを起動すればサムネイル表示ができるので、そちらを使ってしまうのもアリかもしれないと思いついた。

自分は普段、Ubuntu Linuxのデスクトップ環境として Xfce を利用しているので、Xfce の標準ファイラー thunar 以外なら ―― pcmanfm, nautilus, nemo, rox-filer, doublecmd 等なら、MobaXterm経由で起動してWindows10のデスクトップ上に表示できる。
pcmanfm &
nautilus &
nemo &
rox-filer &
doublecmd &

gui_filer_ss.jpg

ちなみに、thunar だけは、MobaXterm から起動すると、Windows10 のデスクトップではなく、アクセス先の実機側のデスクトップ画面に表示されてしまって利用できなかった。これはデスクトップ環境として Xfce を使っているからで…。他のデスクトップ環境を使った場合は、そのデスクトップ環境の標準ファイラーが同じ状態になる。
  • Xfce : 標準ファイラー Thunar
  • Gnome : 標準ファイラー Nautilus
  • LXDE : 標準ファイラー PCManFM

画像ビューアで画像を表示 :

そもそも画像ビューアを使ってしまえばいいのでは、と気が付いた。MobaXterm上で ranger を起動して、画像ファイルの上で Enter を叩いて、例えば画像ビューア sxiv あたりを起動してやれば画像表示ができなくもない。

ranger_sxiv_ss.jpg

ranger から sxiv を起動した場合、sxiv のウインドウ内で右クリックすると、そのフォルダ内に入っている他の画像もサムネイルで一覧表示してくれるので、これでも問題無さそうな気がする…。

#2 [anime] 「すずめの戸締り」を視聴

金曜ロードショーで放送されていたものを録画していたので視聴。新海誠監督作品の劇場アニメ。

面白かった。いや、東日本大震災がキッカケになって作られたこの作品を面白かったと言ってしまっていいのかどうかちょっと悩むけど。こういう形で昇華(?)すること自体はとても良いことだと思えるし…。

映像面では相変わらず見応えがあるなと感心。カメラマップ等を活用しているあたりが興味深い。敵(?)を表現する際のエフェクトも一体どうやって作ってるんだろうなと…。かつて宮崎アニメの「もののけ姫」でもああいった存在をCGで映像化していたわけだけど、随分と進歩が感じられる映像になっていたなと…。スケールも全然違うし…。

ハードウェアで残すか。ソフトウェアで残すか :

そもそも、東日本大震災の記憶を、こういった作品にして後世に残せないかとチャレンジしてみたこと自体が評価に値するのではないかと感じた。たしか以前、監督さんへのインタビュー記事で、「映像作家の自分はあの震災に対してこういう形でしかアプローチできない」といった感じの発言を目にした記憶があるのだけど、映像を見ていて、なるほどたしかにと納得できたというか。

自分達は、過去の大災害の記録を後世に残したいと思った際、一般的にはついついハードウェアで残すことをまず考えてしまう。例えば石碑を残すとか…。しかし、石碑の類は、あっという間に人々の記憶から忘れ去られてしまう。

何せ、大阪の街のど真ん中に、「江戸時代にここまで津波が来たよ…」と記してある碑が残ってるわけで…。どうして街のど真ん中にあるのかと考えたら、まあ、そういうことだよなと…。

あるいは、以下のような話もある…。

_寺田寅彦 津浪と人間
三陸災害地を視察して帰った人の話を聞いた。ある地方では明治二十九年の災害記念碑を建てたが、それが今では二つに折れて倒れたままになってころがっており、碑文などは全く読めないそうである。またある地方では同様な碑を、山腹道路の傍で通行人の最もよく眼につく処に建てておいたが、その後新道が別に出来たために記念碑のある旧道は淋れてしまっているそうである。

寺田寅彦 津浪と人間 より


しかし、それらの記録はハードウェアでしか残せないというわけではない。ソフトウェアで残すという方法もある。

例えば、自分が住んでいる福島県須賀川市には、松明あかしという祭りがある。めっちゃデカイ松明を作ってガンガン燃やすという祭りなのだけど、伊達政宗に滅ぼされた二階堂勢を弔うため、というのが由来。

_松明あかしの由来 / 須賀川市公式ホームページ

災害の記録というわけではないけれど、こういった事例は、ソフトウェアで記録を残そうとした事例と言える。この場合、少なくとも四百年は残せてる。

そして、新海監督が「すずめの戸締り」で試そうとしたのも、こういうことだろうなと…。娯楽性/エンターテイメント性を持った映像作品として提示しつつも、その中で過去に大災害があったことをしっかり示しておくことで、見た人の脳内に情報をインストールする…。「お話」にしてみせることで「覚えておいてね」と語りかける。ソフトウェアで記憶を伝えていこうという、立派な試みの一つと言える。「映像作家には所詮こんなことしかできない」と捉えることもできるだろうけど、「映像作家だからこそ、こんなことだってできる」と捉えることもできる。

何にせよ、後の時代に少しでも伝えていきたいと考えて、実際に作品の形にしてみせただけでも、それは賞賛されてしかるべき行為なのではないかと思えた。こんなこと、作家さんじゃなきゃできない。でも、大半の作家さんはここまでやらない。だけど新海監督は、こうしてやってみせた。それだけでも、自分は監督に拍手を送りたいなと思えたわけで。

ただ、ソフトウェアで残すのは、結構難しい…。それが面白かったら残っていくけど、つまらなかったら残らないから…。このアニメは一体どちらの扱いを受けるのか…。でもまあ、そこは時間が判断してくれることだから…。

2024/04/07() [n年前の日記]

#1 [linux] Linuxのファイラーのアイコンテーマを変更したい

Ubuntu Linux 22.04 LTS (i3-6100T機)に、Windows10 x64 22H2 + MobaXterm からアクセスして、nemo や caja のようなGUIファイラーを起動する時があるのだけど。表示されているアイコンの見た目がどうも気に入らない。おそらくは Yaruテーマが選ばれているのだろうけど、個人的に好みの見た目ではない…。

アイコンテーマを変更したいのだけど、どうやって変更したらいいのだろう。

Ubuntu のデスクトップ画面(Xfce)上なら、設定で「外観」を選んでアイコンテーマを変更すればいいのだけど。MobaXterm から起動した際は別の設定が使われるようで、Xfce側で行った変更が反映されなくて…。

何か方法はあるはずとググっているけど、解決策を紹介してるページには遭遇せず。うーん。

2024/04/08追記 :

~/config/gtk-3.0/settings.ini を修正すれば対応できそう。

#2 [anime] 「ブラック・ジャック 劇場版」を視聴

BS12で放送されていたので視聴。出崎統監督作品。OVA版の流れで作られたアニメらしい。

今はもう見られない映像スタイルなので、その点は興味深く見ることができた。こういうスタイルを今のアニメも部分的に取り込めないものかなあ…。いや、作品によってはデジタル撮影処理で結構再現してる感もあるけれど。今時は、どんなアニメもうっすらとグラデかけてたりするもんな…。

おそらくだけど、漫画から劇画に変わっていく時期を意識させられた世代の作り手達だろうから、このアニメも、漫画的なアニメから劇画的なアニメへの移行を意識しつつ作っていたのではと想像したりもする。そういえば、そもそも原作のBJも、劇画がブームになって、もう漫画は古い、手塚は古いと言われていた時期の作品だったか…。原作が目指してたはずの劇画的な印象を、OVA版も含めてこのシリーズで醸し出せないか挑戦していたのではないかなあ、と。

そのように想像すると、もしかすると今の世代にも応用できるところがありそうな…。なんでもかんでも「漫画原作アニメ」と呼んでしまって大雑把過ぎる分類をしたりせずに、あえて意識して「劇画原作アニメ」と口にしながら作業してみたら最終的な映像の印象も変わってきて他作品との差別化ができたりしないだろうか…。漫画ではなく劇画のように感じるアニメの映像スタイルとは一体どういうものなのか、ちょっと想像してみるのもアリだったりして…? 「呼び方を変えたぐらいでイメージが変わるわけないだろ」と笑われるかもしれないけれど、例えば「少年漫画原作アニメ」と「少女漫画原作アニメ」では思い浮かぶイメージも違ってくるものだし。

それはともかく。BJで90分前後の話を見せるのはなんだかちょっと違うなとも思えた。原作が基本的に1話完結モノだったし、アイデアをポンと出してさっと終わるのがBJっぽいかもなあ、と…。もしかすると、30分で1話、それを3本並べて、みたいな感じでまとめたほうがBJシリーズらしくなったのでは…。でも、劇場版となったら、それなりの尺で作りたいと皆思ってしまうものなのかな…。見る側もそういうフォーマットを期待してそうだし…。

2024/04/08(月) [n年前の日記]

#1 [linux] Linuxのファイラーのアイコンテーマを変更したい。その2

Ubuntu Linux 22.04 LTS (i3-6100T機)に、Windows10 x64 22H2 + MobaXterm からアクセスして、nemo や caja のようなGUIファイラーを起動した際に表示されているアイコンの見た目がどうも気になる。変更したい。

以下のページで紹介されている内容が関係してそうな気がする。

_GTK - ArchWiki
_XDG Base Directory - ArchWiki

以下を変更したら反映されそう。たぶん。
$XDG_CONFIG_HOME/gtk-3.0/settings.ini

ただ、自分の環境では、$XDG_CONFIG_HOME が定義されてなかった。一般的には、$XDG_CONFIG_HOME は ~/.config/ になっているらしいので、~/.config/ を眺めたところ、gtk-3.0 というフォルダがあった。

該当ファイルを修正。
vi ~/config/gtk-3.0/settings.ini
[Settings]?
gtk-application-prefer-dark-theme=0?
gtk-icon-theme-name = Numix?
gtk-theme-name = GreyBird?
gtk-font-name = DejaVu Sans 11?

nemo や caja を起動してみたところ、反映されたように見える。このファイルを弄っていけばどうにかなりそうだな…。

余談。PCManFM-qtなら面倒臭くない。 :

ちなみに、PCManFM-qt なら、PCManFM-qt 自身の設定ウインドウ内にアイコンテーマの選択項目があるので面倒臭くない。

#2 [linux] xfeファイラーについて

xfeというファイラーがあるらしい。X11アプリ。

_32bitパソコンにオススメ(?)ファイルマネージャーXfeが軽快で快適 : 森下巻々ブログ・おきてがみ

Ubuntu Linux 22.04 LTS (i3-6100T機)にインストールして、少しだけ試用してみた。
sudo apt install xfe

xfe と打って実行できる。

試用した感じでは、悪くない気がする。動作もなんとなくだけど軽いような気もするし…。

xfimageについて :

xfe と一緒に、xfimage という画像ビューアもインストールされた。

xfe上で画像ファイルを右クリックして「開く」を選べば、xfimage が起動する。もしくは端末から xfimage と打っても起動する。

右側にファイル一覧が表示されて、左側に画像が表示される。ファイルをダブルクリックしないと表示されないのがちょっとアレな気もするけど…。画像表示するだけならこれでもいいかも。ちなみに画像に含まれているアルファチャンネルは表示に反映されない。透明部分は真っ黒になった。

#3 [cg_tools] アイソメトリックについて実験中

ふと、なんとなく、アイソメトリック(Isometric Projection)の画面で車が走っていくデモプログラムってどうかなと思いついてしまったので、blenderを使って見栄えについて確認中。

大昔のゲーム機は、BG画面がタイルを敷き詰めたような仕組みだったので、アイソメトリックっぽい画面を作るには、パズルのような試行錯誤が必要だったらしいのだけど。今はフツーに3Dで描画できちゃう時代なので…。OpenGL を使えば、平行投影、かつ、カメラの角度を変えるだけでもそれらしく見えるんじゃないのかなと…。

ひとまず、blenderのカメラを透視変換から平行投影に切り替えて、見た目がどうなるか確認。

isometric_test_ss1.jpg

isometric_test_ss2.jpg

isometric_test_ss3.jpg

イケそうな気がする。カメラの角度を30度にしたほうがいいのか、45度にしたほうがいいのか、ちょっと悩むけど…。ググったところ、本来は30度になるっぽい…?

地面に模様を入れたほうがいいのかどうかも悩む。何も入れずに、フラットな見た目にしたほうがいいのかなあ…? でも、コレはチェッカー模様だからアレだけど、例えばキャンバス地だったら印象も変わるのだろうか?

#4 [anime] 「ゆるキャン△ SEASON3」1話を視聴

BS11で放送されていたソレを録画していたので視聴。秋冬キャンプをテーマにした漫画原作をTVアニメ化した作品。

3期から制作スタジオ、メインスタッフが変わって、キャラデザも原作寄りに変更されたそうで。番宣CMを目にして、作画面でちょっと不安になっていたりもしたのだけど…。実際に見てみたらちゃんとゆるキャンシリーズになっているように思えた。何より、時間の進み方が相変わらずおかしい。「アレ? もう終わっちゃうの?」みたいな。キャンプをテーマにしているせいか、見ているこちらの体感時間が通常のアニメのソレとは違ってきてしまうのではないかと思っているけど…。何にせよ、こんな時間感覚になってしまうのだから、これは間違いなくゆるキャンシリーズであろう。みたいな。

キャラデザ変更の是非について :

ネット上の感想を眺めると、キャラデザが変更されたことに不満を持ってる方がチラホラ居るようなのだけど…。メインスタッフがごっそり変わって、1期2期のキャラデザ総作監さんが抜けてしまった/続編にはノータッチになった以上、原作寄りのキャラデザに修正したことは正解だろうと個人的には思えた。

今後、仮に、4期や5期が作られることになったとして。そしてその時も、制作スタジオやメインスタッフがごっそり変わるとしたら、どこかの時点で1期2期のキャラデザとは決別しておかないとマズイ。そして、その時原典として頼るべき絵柄はどれなのかと考えたら、それはどう考えてもアニメ版1期2期ではなく、原作の絵柄が基準になるべきで…。迷ったら原作漫画を開け。そこに答えがあるはずだ。企画を引き継いだスタッフがそう考えるのは、そんなにおかしいことだろうか?

ルパン三世パート1のキャラデザやカリ城の作画監督を担当した大塚康生さんだって、「最近のルパンはカリ城のキャラデザを参考にしたものばかりですよね?」という問いかけに、「そんなことはないですよ。今のルパンのキャラデザをしてる人達も必ず原作を参考にしてキャラデザしてるはずですよ」と答えていたし。まず参考にするのは原作。その上で、過去作の絵柄も参考にして、という形にならないと…。

まあ、ルパン三世アニメ版のキャラデザの違いと比べたら、ゆるキャンのソレなんて「全部同じじゃないですか!?」レベルじゃないかなあ、とも…。「ちがいますよーっ」「これだからしろうとはダメだ! もっとよく見ろ!」と言われそうだけど、両さん達は全バージョンを肯定した上でソレを言ってる点を忘れちゃいけないよなと…。 *1

*1: と言っても全肯定した上で、やれココがどうのアソコがどうのと文句も言ってるはずなんだけど。そういう人種だよな…。

2024/04/09(火) [n年前の日記]

#1 [prog] OpenGLでモデルデータを読み込めそうか調べてる

C/C++ とOpenGLでモデルデータを読み込みたい。そのあたり何か良さそうなライブラリがあったりしないかとググっているけれど…。

Assimpというライブラリがあると知って関連情報を調べてる。

_LearnOpenGL - Assimp

Debian Linux や Ubuntu Linux なら、パッケージがあるっぽい。

_Debian -- bookworm の libassimp-dev パッケージに関する詳細
_Ubuntu - jammy の libassimp-dev パッケージに関する詳細

ただ、MinGW の gcc/g++ で利用する方法が分からない…。

MSYS2 ならパッケージが用意されているように見える。

_Package: mingw-w64-x86_64-assimp - MSYS2 Packages

2024/04/10(水) [n年前の日記]

#1 [prog][cg_tools] OpenGLでモデルデータを読み込めそうか調べてる。その2

C/C++ とOpenGLでモデルデータを読み込みたい。そのあたり何か良さそうなライブラリがあったりしないかとググっているところ。

モデルデータのフォーマットにも色々あるけれど、どのへんがいいだろう。とりあえず、gltf、もしくは、wavefront(obj)形式だろうか。

_glTF - Wikipedia
_Wavefront .objファイル - Wikipedia

gltf用ライブラリ :

gltfの読み込み用ライブラリを探したら、以下に遭遇。

_syoyo/tinygltf: Header only C++11 tiny glTF 2.0 library

まだ試用してないけど、なんだか良さそう。色々なプロジェクトで使われているようでもあるし、Windows + MinGW でも利用できると書いてあるのもありがたい。

obj用ライブラリ :

wavefront(obj)形式の読み込みライブラリを探したら、結構な数が見つかった。テキストファイルだし、フォーマットとしてシンプルなので、対応しやすいのだろう…。

_syoyo/tinyobjloader-c: Header only tiny wavefront .obj loader in pure C99
_tinyobjloader/tinyobjloader: Tiny but powerful single file wavefront obj loader
_Bly7/OBJ-Loader: A C++ OBJ Model Loader that will parse .obj & .mtl Files into Indices, Vertices, Materials, and Mesh Structures.
_tek-nishi/OBJ-loader: Wavefront OBJ loader with OpenGL 1.1, C++
_mojobojo/OBJLoader: stb-style obj loader for obj files output by blender

C/C++で書いたプログラムから、.objを直接読み込んで、解析して利用するようだなと…。

blenderからC言語の形式でエクスポート :

ググっているうちに、blenderから、C言語用の記述でモデルデータを直接エクスポートできるアドオンが存在しているという記事を見かけた。

_Blender OpenGL / C header exporter - ゲームが作れるようになるまでがんばる日記
_でらうま倶楽部 : Blenderのアドオン C header exporter がありがたい
_export - Get indices of vertices of triangulated faces in python - Blender Stack Exchange

しかし配布サイトは404。WebArchiveにページだけは残っていたけど…。

_Blender OpenGL / C header exporter... (WebArchive)

肝心のアドオンファイルは残ってなかった。ファイル名は、io_mesh_ogl-2.63.tar.gz、もしくは io_mesh_ogl_2.58.tar.gz だったようだけど…。

まあ、blender 2.63 時代のアドオンらしいので、今現在の blender では利用できない可能性が高いけど、どういう作りだったのかちょっと気になる。入手できないのは残念。

objをcに変換 :

wavefront(obj)形式を読み込んで、C/C++ の形に変換できるツールがあれば済むのではないかと思いついた。ググってみたら、やはりそういうツールを作った事例がある模様。

_glampert/obj2c: Simple command line tool to convert Wavefront OBJ models to C/C++ data arrays.
_Moon64/obj2c.py at main ・ UnderVolt/Moon64

C++で書かれた前者を試用してみることにした。環境は Windows10 x64 22H2 + MSYS2 (h++ 13.2.0)。

任意のフォルダを作成して、中に入って、githubからクローン。
mkdir obj2c
cd obj2c
git clone https://github.com/glampert/obj2c.git
cd obj2c

MSYS2 の g++ 13.2.0 を使ってビルドする。以下で、obj2c.cpp から obj2c.exe が作れる。
g++ -o obj2c.exe obj2c.cpp -static

注意点。MinGW g++ 6.3.0 でビルドしようとしたらエラーが出た。MSYS2 の g++ 13.2.0 ならエラーが出なかったし、exe も生成された。

使い方は以下。最後につけてる -c は、配列の個数も出力させるオプション。
obj2c.exe INPUT.obj OUTPUT.c -c

ちなみに、-h か --help をつければヘルプが表示される。
> obj2c.exe --help
Convert Wavefront OBJ mesh file to C/C++ data arrays.

Usage:
  $ obj2c.exe <source-file> <target-file> [options]
Options:
  -h, --help            Shows this help text.
  -v, --verbose         Be verbose; output a lot of info and timings.
  -s, --static_arrays   If present, add the 'static' qualifier to array declarations.
  -c, --write_counts    Write lengths of data arrays as constants.
      --inc_file[=name] If flag present, generate an include file externing the array variables.
                        Incompatible with 'static_arrays'. If no filename provided, uses the target file name.
  -n, --smooth_normals  If set, gen smooth per-vertex normals. Default are shared per-face 'flat' normals.
  -f, --vb_friendly     Make the output 'Vertex Buffer friendly'. That is, single index per-vertex.
      --ib_type=<type>  Index buffer data type for when using 'vb_friendly'.
                        Possible values are 16, 16std, 32 and 32std.
                        The 'std' suffix causes the use of the standard C data types found in <stdint.h>
      --no_uvs          Don't output mesh UVs, even if they are present in the OBJ file.

Created by Guilherme R. Lampert, Apr 10 2024.

それはさておき。blender 3.6.9 x64 LTS からエクスポートした .obj を変換してみたところ、たしかに頂点座標の配列等は得られたのだけど…。面毎に異なるマテリアルを指定しているのに無視されて、面情報は全部一つにまとめられてしまうようだなと…。

blenderからobjをエクスポート :

blender 3.6.9 から wavefront (obj)形式でエクスポートしたものの、マテリアルファイル (.mtl) の中にカラー情報が出てこなくて結構悩んでしまった。しかし、これは単に自分の勘違いだった。RGB を 1.0 or 0.0 にして実験していたから一見分かりづらかっただけで、.mtl の Kd (diffuse, ディフューズ色)に、blender のマテリアルのベースカラーが反映されていることが確認できた。

Ks (Specular, スペキュラ色)は、blender のスペキュラーが対応してるようだなと…。ただ、blender上ではRGB別で指定できないように見える。また、Ns (スペキュラー指数)もどこで指定すればいいのやら…。

Ka (Ambient, アンビエント色)は、blender のどこで指定すればいいのか分からない…。

#2 [nitijyou] 床下の水を組み上げようとした

昨日かなりの雨が降ったのだけど。「床下に水が溜まっているのではないか。汲み出せ」と親父さんが言い出したので、朝から床下の水の汲み出し作業をしてみた。

水を溜めておく堀(?)が満杯になって溢れていた…。横に置いてあった電動ポンプの電源供給部まで水に浸かっていた。ここまで水はこないだろうと安易に思って置いてたのだけど、失敗した…。何か台でも設置して、その上に置いておけばよかった。

汲み出しを始めてみたけれど、全く水位が変化しない。08:00-17:00まで動かしてみたけど、1cm下がったか下がってないかというレベル。

親父さんが、普段風呂の水を畑に持っていくために使っている電動ポンプ、TERADA SL-52 の存在を思い出してくれて、庭の水撒き用ホース6mを繋いで汲み出し状況を改善してくれた。

しかし、堀が空になっても、物凄い勢いで周りから水が流れ込んでくる…。10秒ぐらいでまた堀が満杯に。この勢いでは小さい電動ポンプで汲み出せるわけがない。これじゃまるで川だか沼だかの中に家が建ってるような状況…。

電動ポンプを物色 :

今回のような状況になった場合、それなりのスペックの電動ポンプが必要だなと…。今まで使ってた小さい電動ポンプでは全く歯が立たない。しかし、どのくらいの値段の製品を購入すればいいのだろうか…。

今回最終的に使った TERADA SL-52 の価格を調べてみたけど、既に生産終了品になっていて値段は分からなかった。ただ、現行品の SL-40 が12,000 - 18,000円ぐらいで販売されているようなので、SL-52 も同じくらいか、もしくはもっと高い値段だったのではないかと…。

中国メーカの製品なら 1,980円ぐらいから売られているけれど、価格差からして罠があるとしか思えない…。表記スペックを満たしてないとか、すぐに壊れるとかありそうだよな…。

2024/04/11(木) [n年前の日記]

#1 [prog][cg_tools] OpenGLでモデルデータを読み込めそうか調べてる。その3

C/C++ とOpenGLでモデルデータを読み込みたい。

wavefront形式(.obj)を読み込んでC言語の配列の形で出力する Pythonスクリプトを書き始めた。頂点情報、UV情報、法線情報を配列の形にして出力するところまではできたけど、複数のマテリアルを割り当てた .obj を読み込んだ際に面情報をどう並べたらいいのかで悩んでしまった。 何にせよ、glDrawElements() の使用例を眺めてみないと…。

Wings3Dでobjをエクスポート :

Wings3D 2.3 x64 で wavefront (.obj) をエクスポートしてみたら、中に「s 1」という行があって悩んだ。blender でエクスポートした .obj には、そんな行は無いのに…。

_Wavefront .objファイル - Wikipedia

「s 1」はスムージングを有効にする指定らしい。たしかに、Wings3D でエクスポートした .obj を blender 3.6.9 x64 LTS にインポートすると、なんだかスムージングがかかっているような見た目になる。「s 1」が入っていたから ―― スムージングを有効にする行が入っていたから、そんな状態になっていたのか…。

Wings3D上でスムージングを無効にする方法はないのだろうか…。TABキーを叩くとウインドウ上でスムージングの有効無効が切り替わるけれど、これはあくまで作業ウインドウ上の見た目を変えるためだけの機能のようで、エクスポートした .obj には反映されなかった。

線を全選択して、右クリック → エッジ属性の切替 → ハードエッジにする、を選んだら、スムージングがかかってない見た目になった。しかし、コレを .obj としてエクスポートすると、「s 2」「s 3」という行が盛り込まれてしまう…。「s off」になるわけではないのか…。

2024/04/12(金) [n年前の日記]

#1 [cg_tools] XISMO2 や Metasequoia 3.x を試用中

3Dモデルデータを作成して、wavefront (.obj) でエクスポートして、どんな内容になるのか確認したい。

Windows10 x64 22H2上で、blender 3.6.9 x64 LTS と Wings3D 2.3 を使って試していたけど、他の3DCGソフトならまた違う結果が出力されるのではないかと気になってきたので、XISMO2 225a と Metasequoia 3.1.6 をインストールして、どんな .obj がエクスポートされるのか確認することにした。

XISMO2 は無料で使える3DCGモデリングソフト。.obj でエクスポートすることができる。XISMO_225a.zip を入手して解凍して、今回は D:\Prog\XISMO2\ に置いた。

_XISMO


Metasequoia は有償のモデリングソフト。自分は 3.x 時代に購入。4.x になったら価格がグンと上がってしまったので、そちらは購入していない。無料で試用できる版もあるけれど、その版は mqo形式でしか保存できない。

_metaseq.net | 3Dモデリングソフトウェア「Metasequoia(メタセコイア)」公式サイト

metaseq316.zip を入手して解凍。D:\Prog\Metasequoia3\ に置いた、とメモ。

Metasequoia 3.1.6 からエクスポートした .obj を blender 3.6.9 x64 LTS にインポートしたら左右がおかしくなってしまった。Metasequoia から .obj でエクスポートする際は、x軸を反転したほうが良さそう。ついでに、拡大縮小率を 0.01 ぐらいにしておいたほうがいいのかもしれない。

#2 [cg_tools] Shade12で各面に異なる材質を割り当てたい

Windows10 x64 22H2 + Shade 12.1.0 Standard で、wavefront (.obj) のエクスポートの実験をしていたのだけど、ポリゴンメッシュで作られたオブジェクトの、それぞれの面に異なる材質(色)を割り当てられないものかと悩んでた。

以前の自分は、違う材質を割り当てたい部分を、オブジェクトから分離して ―― オブジェクトを複製して、不要な部分を削除して、必要なところだけ残して、そこに別の材質を割り当ててから全オブジェクトを結合していたらしいのだけど…。

_mieki256's diary - Shade12を試用中その4

今回ググってみたら、もっと簡単なやり方があった。フェイスグループなるものを利用すれば、ポリゴンメッシュの各面に任意のマスターサーフェイスを割り当てられるらしい。

_フェイスグループとは ? - Shade3D Knowledge Base
_フェイスグループを追加する - Shade3D Knowledge Base

一応手順をメモ。
  1. 事前に、必要な分のマスターサーフェイスを作成しておく。
  2. ポリゴンメッシュを選択。かつ、Mキーを押して「形状編集」モードにする。これで任意のポリゴンを選択できるようになる。
  3. 色を変えたいポリゴンをクリックして選択。
  4. 統合パレットの形状情報タブに切り替える。「情報」と書いてあるアイコンをクリックすれば選べる。
  5. 形状情報タブの下のほうにある「フェイスグループ情報」の、下のほうにある「追加」ボタンをクリック。これで、選択中のポリゴンがグループ化される。
  6. 「追加」ボタンのすぐ右にあるところでマスターサーフェイスが選べる。

shade12_facegrp_ss01.png

shade12_facegrp_ss02.png

shade12_facegrp_ss03.png

余談。マスターサーフェイスを複製したい :

マスターサーフェイスを用意する際にオブジェクトを選択して材質を削除して、とかやるのが面倒臭かった。マスターサーフェイスって複製できないのかな…。

できるらしい。

_マスターサーフェスを複製したい - Shade3D Knowledge Base

マスターサーフェイスを選択した状態で、表面材質パネルの「複製」をクリックすれば複製できた…。それだけで良かったのか…。

shade12_facegrp_ss04.png

shade12_facegrp_ss05.png

shade12_facegrp_ss06.png

2024/04/13() [n年前の日記]

#1 [nitijyou] 電動ポンプについて物色中

床下の水を汲み取るための電動ポンプを、もうちょっと強力なモノにしたほうがいいのかなと思えてきて、どんな製品がありそうかググって物色中。

今まで使っていたのは、バスポンプ 湯ポポン、なる製品だと思う。ACアダプタ部分に貼ってあるシールには「BATH PUMP 湯ポポン」としか書いてないけれど、ググった感じでは、おそらく、ミツギロン 湯ポポン10 BP-40 なる製品ではないかなと。形もよく似ているし。吐水量は 8L/min らしい。1時間なら、480L/h だろうか。

家庭用の電動ポンプと言うと、10,000円ぐらいする「水中ポンプ」と呼ばれるジャンルと、風呂場の浴槽から水を汲み出して別の何かに使うための「バスポンプ」というジャンルがある模様。吐水量は「水中ポンプ」が圧倒的に多く、「バスポンプ」は一般的に8〜10L/min がフツーっぽい。ただ、バスポンプの中にも、吐水量が2倍/3倍になったと謳う製品もあって、14 - 23L/min のモノも見かけた。

ポンプの排水コネクタも数種類あるようで。コネクタ部分の外径が、13mm, 16mm, 19mm, 22mm, 25mm 等々。16mm のものには、内径が15mmのホースを使うのだろうか。内径16mmのホースはちょっと見つからないし、あちこちの製品ページに、「内径15mmの散水ホースを利用できる」と書いてあったりもするので、おそらく内径15mmのホースがメジャーな扱いになっているのだろう。たぶん。

2024/04/14() [n年前の日記]

#1 [prog][cg_tools] OpenGLでモデルデータを読み込めそうか調べてる。その4

C/C++ とOpenGLで、何かしらのモデルデータを読み込んで描画したい。

wavefront形式(.obj)を読み込んで、C言語の配列の形で出力する Pythonスクリプトを書いてるところ。複数のマテリアルを割り当ててある場合は、頂点カラーにマテリアルの Kd 値を割り振るようにしてみた。

glDrawElements()はビミョーに使えない印象 :

OpenGL 1.1 の glDrawElements() について調べてる。

OpenGL 1.1 で頂点配列を使ってポリゴンを描画する場合、glDrawArrays() か glDrawElements() を使えるらしいのだけど。
  • glDrawArrays() ... 頂点配列を渡して描画する。
  • glDrawElements() ... 頂点配列のインデックス値の配列を渡して描画する。

頂点配列のインデックス値を渡して描画するほうが、頂点配列内の座標値が重複したりしないので、データ量は少なくて済む。実際、wavefront形式も、頂点座標群とは別に、面を構成する情報としてインデックス値群を持っているので、このメリットを意識したフォーマットになっている。

しかし各頂点には、頂点座標の他にも、法線情報(Normal情報)、テクスチャのUV座標、頂点カラー情報も持たせないといけないはずで…。

wavefront形式の場合、頂点配列、法線情報、テクスチャのUV座標のインデックス値が、別々の値になってる場面がほとんど。しかし、OpenGL の glDrawElements() のサンプルコードを見る限り、どうやら頂点、法線、UVで、別々のインデックス値を指定することはできないようで…。となると、wavefront形式が持っている面情報 = インデックス値列をそのまま OpenGL に流用することはできないようだなと…。

仕方ないので、Pythonスクリプト側で、頂点座標、法線情報、UV座標、頂点カラーを、全部配列にずらずらと展開してから出力してしまうことにした。それらの配列を使って、glDrawElements() ではなく、glDrawArrays() で描画する。

こういうデータの持ち方では、座標値が重複するのでデータ量はかなり増えてしまうけど、これはもうどうしようもないかなと…。いやまあ、glBegin() と glEnd() を使って、ポリゴンを1枚1枚ループを回して描画するようにすれば、wavefront形式が持ってる面情報をそのまま使うこともできるだろうけど、その代わり処理が遅くなってしまうはず。データは増えるけど処理が速いか、データは減るけど処理が遅いか、どっちを取るか、という話になるのだろうか。

本当に頂点配列を使うと処理は速くなるのかな。ベンチマークを取ったわけじゃないから確実なことは言えない気もする…。

2024/04/15(月) [n年前の日記]

#1 [prog][cg_tools] OpenGLでモデルデータを読み込めそうか調べてる。その5

C/C++ とOpenGLで、何かしらのモデルデータを読み込んで描画したい。

wavefront形式(.obj)を読み込んで、C言語の配列の形で出力する Pythonスクリプトは書けたので、C言語でOpenGLを使って描画するソースを書いて動作確認中。一応描画はできたような気がする。明日の日記でまとめよう…。

2024/04/16(火) [n年前の日記]

#1 [prog][cg_tools] OpenGLでモデルデータを読み込めそうか調べてる。その6

C/C++ とOpenGLで、何かしらのモデルデータを読み込んで描画したい。

今回は、wavefront形式(.obj)を Pythonスクリプトで読み込んで、C言語の配列の形で出力して、それを使って OpenGLで描画するC言語のソースを書いてみた。C/C++ でモデルデータファイルを読み込んでいるわけではないけれど、.exeの中にモデルデータを含めてしまいたい場合は、こういうやり方でもいいんじゃないかなあ、と…。

動作確認環境は以下。
先に実行結果を。今回作成した 01_drawobj.exe を実行すると以下のような見た目になる。モデルデータを描画することができている。

使用するモデルデータ :

blender 3.6.9 x64 LTS で簡単なモデルデータを作成して、wavefront形式(.obj)でエクスポートした。ファイル → エクスポート → Wavefront (.obj) を選択。
  • .objファイルは、頂点座標、テクスチャのUV座標、法線情報、面情報が保存されているファイル。
  • .mtlファイルは、マテリアル情報が保存されているファイル。

blender から .obj をエクスポートする際、四角形を三角形にするオプション(メッシュの三角面化)にチェックを入れる。後で出てくる Pythonスクリプトも、C言語のソースも、三角形ポリゴンにのみ対応させてある状態なので…。

作成したモデルデータは以下。一つは、只の四角い箱。各面が別の色になっている。

_cube01.obj
_cube01.mtl

cube01_ss01.gif


以下のモデルデータは、blenderでお馴染みのスザンヌ。目や口のあたりを別の色にしている。

_suzanne01.obj
_suzanne01.mtl

suzanne01_ss01.gif

PythonスクリプトでC言語の配列に変換 :

この wavefront形式(.obj) と .mtl を、Pythonスクリプトを使って、C言語の配列の形に変換して出力する。今回書いたPythonスクリプトは以下。ライセンスは CC0 / Public Domain ということで…。

_pyobj2c.py

使用方法は以下。
python pyobj2c.py INPUT.obj > OUTPUT.h

例えば hoge.obj というファイルを読み込ませた場合は、以下の名前の配列を作成する。
  • const float hoge_obj_vtx[] ... 頂点配列。1つにつき、{x, y, z} を持つ。
  • const float hoge_obj_nml[] ... 法線情報配列。1つにつき、{x, y, z} を持つ。
  • const float hoge_obj_uv[] ... テクスチャ座標配列。1つにつき、{x, y} を持つ。
  • const float hoge_obj_col[] ... 頂点カラー配列。1つにつき、{r, g, b, a} を持つ。
  • const unsigned int hoge_obj_vtx_size ... 頂点配列の個数
  • const unsigned int hoge_obj_nml_size ... 法線情報配列の個数
  • const unsigned int hoge_obj_uv_size ... テクスチャ座標配列の個数
  • const unsigned int hoge_obj_col_size ... 頂点カラー配列の個数
OpenGL 1.1 の glDrawArrays() を使って描画することを前提にした配列になっている。ちなみに、頂点カラーについては、マテリアル情報の Kd (Diffuse色)だけを取り出して頂点カラーにしている。


前述の2つの .obj を変換。以下の結果が得られた。

_cube01.h
_suzanne01.h

これらの配列を、C言語のソースで #include して利用する。

C言語のソース :

C言語のソースは以下。ライセンスは CC0 / Public Domain ってことで。ちなみに、ウインドウの作成等はGLFW3を利用している。

_01_drawobj.c
#include <stdlib.h>
#include <stdio.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GLFW/glfw3.h>
// #include <SOIL/SOIL.h>

#include "cube01.h"
#include "suzanne01.h"

#define WDW_TITLE "Draw wavefront obj"

// Window size
#define SCRW 1280
#define SCRH 720

#define FOV 50.0

#define ENABLE_LIGHT 1

#if ENABLE_LIGHT
const float light_pos[4] = {1.0, 1.0, 1.0, 0.0};
const float light_ambient[4] = {0.2, 0.2, 0.2, 1.0};
const float light_diffuse[4] = {0.8, 0.8, 0.8, 1.0};
const float light_specular[4] = {0.7, 0.7, 0.7, 1.0};
#endif

typedef struct
{
  int scrw;
  int scrh;
  float fovy;
  float znear;
  float zfar;

  double angle;
} GWK;

static GWK gw;

// ----------------------------------------
// Render
void render(void)
{
  gw.angle += (45.0 / 60.0);

  // init OpenGL
  glViewport(0, 0, gw.scrw, gw.scrh);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(gw.fovy, (double)gw.scrw / (double)gw.scrh, gw.znear, gw.zfar);
  glMatrixMode(GL_MODELVIEW);

  // clear screen
  glClearColor(0, 0, 0, 1);
  glClearDepth(1.0);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glDepthFunc(GL_LESS);
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_BLEND);
  glEnable(GL_NORMALIZE);

  glLoadIdentity();

#if ENABLE_LIGHT
  glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
  glEnable(GL_COLOR_MATERIAL);

  glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
  glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
  glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
#endif

  // obj move and rotate
  glTranslatef(0.0, 0.0, -10.0);
  float scale = 3.0;
  glScalef(scale, scale, scale);
  glRotatef(-20.0, 1, 0, 0);
  // glRotatef(gw.angle * 0.5, 1, 0, 0);
  glRotatef(gw.angle, 0, 1, 0);

  glEnable(GL_CULL_FACE);
  glCullFace(GL_BACK);

  // draw vertex array

  glEnableClientState(GL_VERTEX_ARRAY);
  // glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  glEnableClientState(GL_NORMAL_ARRAY);
  glEnableClientState(GL_COLOR_ARRAY);

#if 1
  glVertexPointer(3, GL_FLOAT, 0, suzanne01_obj_vtx);
  // glTexCoordPointer(2, GL_FLOAT, 0, suzanne01_obj_uv);
  glNormalPointer(GL_FLOAT, 0, suzanne01_obj_nml);
  glColorPointer(4, GL_FLOAT, 0, suzanne01_obj_col);
  glDrawArrays(GL_TRIANGLES, 0, suzanne01_obj_vtx_size);
#else
  glVertexPointer(3, GL_FLOAT, 0, cube01_obj_vtx);
  glNormalPointer(GL_FLOAT, 0, cube01_obj_nml);
  glColorPointer(4, GL_FLOAT, 0, cube01_obj_col);
  glDrawArrays(GL_TRIANGLES, 0, cube01_obj_vtx_size);
#endif

  glDisableClientState(GL_COLOR_ARRAY);
  glDisableClientState(GL_NORMAL_ARRAY);
  // glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  glDisableClientState(GL_VERTEX_ARRAY);
}

// ----------------------------------------
// init animation
void init_animation(int w, int h)
{
  gw.scrw = w;
  gw.scrh = h;
  gw.angle = 0.0;

  gw.fovy = FOV;
  gw.znear = 1.0;
  gw.zfar = 1000.0;

  glViewport(0, 0, (int)gw.scrw, (int)gw.scrh);
}

// ----------------------------------------
// Error callback
void error_callback(int error, const char *description)
{
  fprintf(stderr, "Error: %s\n", description);
}

// ----------------------------------------
// Key callback
static void key_callback(GLFWwindow *window, int key, int scancode, int action, int mods)
{
  if (action == GLFW_PRESS)
  {
    if (key == GLFW_KEY_ESCAPE || key == GLFW_KEY_Q)
    {
      glfwSetWindowShouldClose(window, GLFW_TRUE);
    }
  }
}

// ----------------------------------------
// window resize callback
static void resize(GLFWwindow *window, int w, int h)
{
  if (h == 0)
    return;

  gw.scrw = w;
  gw.scrh = h;
  glfwSetWindowSize(window, w, h);
  glViewport(0, 0, w, h);
}

// ----------------------------------------
// Main
int main(void)
{
  GLFWwindow *window;

  glfwSetErrorCallback(error_callback);

  if (!glfwInit())
  {
    // Initialization failed
    exit(EXIT_FAILURE);
  }

  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 1); // set OpenGL 1.1
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);

  // create window
  window = glfwCreateWindow(SCRW, SCRH, WDW_TITLE, NULL, NULL);
  if (!window)
  {
    // Window or OpenGL context creation failed
    glfwTerminate();
    exit(EXIT_FAILURE);
  }

  glfwSetKeyCallback(window, key_callback);
  glfwSetWindowSizeCallback(window, resize);

  glfwMakeContextCurrent(window);
  glfwSwapInterval(1);

  // Init OpenGL
  int scrw, scrh;
  glfwGetFramebufferSize(window, &scrw, &scrh);
  init_animation(scrw, scrh);

  // main loop
  while (!glfwWindowShouldClose(window))
  {
    render();
    glfwSwapBuffers(window);
    glfwPollEvents();
  }

  glfwDestroyWindow(window);
  glfwTerminate();
  exit(EXIT_SUCCESS);
}

render() の中で OpenGL の描画をしている。glDrawArrays() の前後を見れば、今回のモデルデータの利用の仕方が分かるだろうか…。

  glEnable(GL_CULL_FACE);
  glCullFace(GL_BACK);

  glEnableClientState(GL_VERTEX_ARRAY);
  // glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  glEnableClientState(GL_NORMAL_ARRAY);
  glEnableClientState(GL_COLOR_ARRAY);

  glVertexPointer(3, GL_FLOAT, 0, suzanne01_obj_vtx);
  // glTexCoordPointer(2, GL_FLOAT, 0, suzanne01_obj_uv);
  glNormalPointer(GL_FLOAT, 0, suzanne01_obj_nml);
  glColorPointer(4, GL_FLOAT, 0, suzanne01_obj_col);

  glDrawArrays(GL_TRIANGLES, 0, suzanne01_obj_vtx_size);

  glDisableClientState(GL_COLOR_ARRAY);
  glDisableClientState(GL_NORMAL_ARRAY);
  // glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  glDisableClientState(GL_VERTEX_ARRAY);
  • glEnableClientState() で、利用したい情報を有効にする。
  • GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY, GL_NORMAL_ARRAY, GL_COLOR_ARRAY 等を指定することで、頂点座標配列、テクスチャ座標配列、法線情報配列、頂点カラー配列を有効にできる。
  • glVertexPointer()、glTexCoordPointer()、glNormalPointer()、glColorPointer() で、利用したい頂点配列等を指定できる。
  • glDrawArrays() で、頂点配列を使って描画。
  • glDisableClientState() で、利用したい情報を無効化。


Makefileは以下。MinGW gcc 6.3.0、MSYS2 gcc 13.2.0、Ubuntu Linux 22.04 LTS + gcc 11.4.0 でビルドできることを確認した。

_Makefile
MODELS = cube01.h suzanne01.h
SRCS = 01_drawobj.c $(MODELS)

ifeq ($(OS),Windows_NT)
# Windows
TARGET = 01_drawobj.exe
GCC_VERSION=$(shell gcc -dumpversion)

ifeq ($(GCC_VERSION),6.3.0)
# MinGW gcc 6.3.0
LIBS = -static -lopengl32 -lglu32 -lwinmm -lgdi32 -lglfw3dll -mwindows
else
# MinGW gcc 9.2.0, MSYS2
LIBS = -static -lopengl32 -lglu32 -lwinmm -lgdi32 -lglfw3 -mwindows
endif

else
# Linux (Ubuntu Linux 22.04 LTS, gcc 11.4.0)
TARGET = 01_drawobj
LIBS = -lGL -lGLU -lglfw -lm
endif

$(TARGET): $(SRCS) Makefile
    gcc -o $@ $(SRCS) $(LIBS)

%.h: %.obj
    python pyobj2c.py $< > $@

.PHONY: clean
clean:
    rm -f *.o $(TARGET) $(MODELS)

ちなみに、MinGW gcc 6.3.0 でビルドすると、別途 glfw3.dll が必要になるけれど、MSYS2 gcc 13.2.0 でビルドすれば GLFW3 をスタティックリンクできるので、MSYS2 を使って実験したほうがいいと思う。

以下を打ってビルド。
make clean
make
01_drawobj.exe が生成される。

2024/04/17(水) [n年前の日記]

#1 [prog] QGISを試用中

OpenGLで道路を描画するために、道路データが欲しい。

道路データを自動生成しようかとも思ったけれど、現実世界の道路を描画したほうがいいような気がしてきた。現実世界の道路なら、妙な交差をしてしまったりしないはずだし。しかしそのためには、現実世界の道路データが必要で…。

ググってみたら、QGISなるソフトを利用すればその手のデータを得ることができるらしいので、試用してみることにした。

環境は Windows10 x64 22H2。

参考ページ :

QGISをインストール :

以下のサイトから、QGIS を入手できる。無料で利用可能。クロスプラットフォームのアプリなので、Windows、Mac、Linux上で動作する。らしい。

_QGISプロジェクトへようこそ!

最初は、QGIS 3.34.5-1 LTR (QGIS-OSGeo4W-3.34.5-1.msi) を入手してインストールしたのだけど、このバージョンは GSI-VTDownloader というプラグインがエラーを出して動かなかった。バグ報告も上がっていて、Shapely なるモジュールが 2.0.0以上になっているとダメらしい。2.0.0未満の Shapely を使っている、古いバージョンのQGISを使うのが手っ取り早い回避策とのこと。

_QGIS 3.28、3.30でメソッド呼び出し時のエラーが発生します - Issue #9 - Kanahiro/GSI-VTDownloader

QGISのダウンロードページの「全てのリリース」を辿って、旧バージョンを入手できる。

_QGISのダウンロード

件のプラグインを紹介しているページの記述によると、QGIS 3.22.10 なら動いていたらしい。バージョンが書かれていて助かった…。ありがたや。QGIS-OSGeo4W-3.22.10-1.msi を入手してインストールし直した。

ちなみに、セットアップファイルは 1.0GB - 1.2GB。インストール後は、2.31GB。かなり容量を食う…。

デスクトップにフォルダが作成されて、中にショートカットファイル群が入ってた。QGIS Desktop 3.22.10 というショートカットファイルをダブルクリックして実行。

GSI-VTDownloader プラグインをインストール :

GSI-VTDownloader プラグインを利用すると、地図を表示して、その範囲の道路データを線情報として取得できるらしい。導入してみる。

プラグイン → プラグインの管理とインストール、を選択。検索欄に「GSI」と打ち込むと、GSI-VTDownloader がリストアップされるので、選択して、「インストール」をクリック。これでインストールできた。

念のために再度書いておくけれど、インストール時にエラーが出る場合は、前述したように QGIS のバージョンが新し過ぎる可能性がある。

QGIS + GSI-VTDownloaderの使い方 :

  • 左側のブラウザ上で「XYZ Tiles」 → OpenStreetMap をダブルクリック。
  • 世界地図が表示されるので、マウスホイールを回転させて拡大縮小したり、中ボタン(ホイールボタン)ドラッグで位置を変更したりして、目的の場所を表示。

  • ツールバー上の黒い日本地図っぽいアイコンをクリックすると、GSI-VTDownloader のダイアログが開く。
  • Source-layer で「road 道路(線)」を選び、Zoomlevelを14ぐらいにする。
  • 「矩形範囲で実行」を選んで、欲しい場所をマウスドラッグで指定。
  • roadというレイヤーができて、道路情報が線で得られたような表示になった。

必要な部分だけを残す :

欲しい道路部分だけを残した状態にしたい。

まず、roadレイヤーを選んで、ベクタ → 空間演算ツール → 融合 (dissolve)、を選ぶ。これで途切れていた線が繋がるらしい。

「融合ポリゴンの出力」というレイヤーができるので、レイヤー名を右クリックして「編集モード切替」を選ぶか、ツールバー上の黄色い鉛筆っぽいアイコンをクリック。これで頂点を移動したり削除したりできるモードになるらしい。

ツールバー上の「Vertex Tool」のアイコンをクリックすれば、頂点を選択できる状態になる。

マウスドラッグで領域を選択すると、頂点に青い丸がついて選択状態になるので、不要な線を選択してDeleteキーで削除。

これで必要なところだけ残した状態にできた。

さて…この線の座標値をエクスポートするにはどうしたら…。

#2 [zatta] 地図を眺めて道路を探してた

OpenGLで道路を描画する際に、現実の道路のデータを利用するとして、一体どのあたりの場所がいいだろうかと、地図を眺めて検討してた。

鳳坂峠近辺 :

たしか福島県のどこぞの道路で、「カーブが異様に多いけれど冬は凍ってしまって非常に危ないからトンネルを掘ってバイパスを作った」という話があった気がする。

逆に考えると、そのくらいカーブが多いなら、画面に出してスクロールしていくだけでも変化に富んでイイ感じになるかもと思えてきた。しかし、一体どこの道路だったか…。

ググってみて場所が分かった。

_2.5kmの新トンネルで峠のくねくね道を通過! 福島の国道118号「鳳坂工区」11月開通 | くるまのニュース
_鳳坂バイパス - Wikipedia
_鳳坂峠,トンネル工事 | 幡谷自然農園 日記 - 楽天ブログ
天栄のイロハ坂みたいな坂で「這う坂(はうさか)」で鳳坂とか。

鳳坂峠,トンネル工事 | 幡谷自然農園 日記 - 楽天ブログ より


なるほど。国道118号線。鳳坂峠。メモしておこう…。

Google Map をこの日記ページに埋め込めるだろうか…?




Googleストリートビューで眺めると、旧道の出入り口は封鎖されてしまっているようで…。なんだかもったいない。いやまあ、バイパスが既にあるのに、今更旧道のメンテナンス費用とかかけてられないから潰しちゃうのが妥当なのかもしれんけど。

_Googleストリートビュー 東側
_Googleストリートビュー 西側

ちなみに、Googleストリートビュー上で、まだ旧道の写真は残っていた。今も存在しているけど通れない、幻の道路ということになるのかな。

しまりんの実家があるとされる場所を調べた :

「ゆるキャン△」の登場キャラ、しまりんの実家と、アニメ版1話でしまりんがキャンプした場所がどのへんにあるのかも調べてた。聖地巡礼をしてらっしゃる方がblog上で詳しい情報を残してくれていたのでとても助かった。ありがたや。

_ゆるキャン△の聖地を行く3 その7 本栖みちから古関へ - 気分はガルパン、ゆるキャン△
_ゆるキャン△の聖地を行く3 その8 甲斐常葉の禅刹 - 気分はガルパン、ゆるキャン△
_ゆるキャン△の聖地を行く3 その4 本栖湖へ - 気分はガルパン、ゆるキャン△
_ゆるキャン△の聖地を行く3 その5 浩庵キャンプ場 - 気分はガルパン、ゆるキャン△

しまりんの実家があるとされている場所から、本栖湖の浩庵キャンプ場までのルートは、カーブがそれなりにあって変化に富んでるだろうから、これもイイ感じになりそうだなと…。もしかすると、本栖湖のシルエットも画面に出したら更に雰囲気が出るかもしれない。

ただ、Google Map を眺めると、途中で不思議な道路が…。ぐるっと一周してるけど、これは一体何だろう…。トンネル? バイパス? 何故一周させてるんだろう…?




甲州いろは坂とやらもなんだか良さそうな…。

_秋の「甲州いろは坂」が、ライダー・ドライバーに人気〜 色とりどりの紅葉をまとう、つづら折りの坂道 〜|身延町役場のプレスリリース

それにしても、しまりんは本当にこの距離を自転車で…? もっとも、Google Mapで距離を出してみたら10kmぐらいだった。そんなもんか…。高校生ならそのくらいの距離は余裕で走れるか…。今頃になって確認してみたけど、運動音痴の自分ですら高校時代は毎日片道15kmを自転車で走って通ってたみたいだし。「ゆるキャン△」のソレも、メインキャラ達の若さを伝えるエピソードの一つ、だったのかもしれないなと…。

2024/04/18(木) [n年前の日記]

#1 [prog] QGISを試用中。その2

OpenGLで道路を描画するために、道路データが欲しい。QGISというソフトを使うと、現実世界の道路データを取得できるので、試用しているところ。

環境は、Windows10 x64 22H2 + QGIS 3.22.10 LTR。GSI-VTDownloader プラグインも利用している。

QGISのベクタレイヤ上の座標をエクスポート :

_昨日、 道路の線データ(ラインデータ)を、QGIS上で表示するところまではできた。

qgis_ss_20240418_01.png


このラインデータの座標値を、テキストファイル等で出力したい。

以下が参考になった。ありがたや。

_shpファイル(XY座標データ)をCSVファイル(緯度経度)で保存したい


一応手順をメモ。レイヤー名を右クリック → エクスポート → 新規ファイルに地物を保存、を選ぶ。

qgis_ss_20240418_02.png


「ベクタレイヤを名前を付けて保存」というダイアログが開く。
  • 形式は、「カンマで区切られた値[CSV]」を選ぶ。
  • ファイル名を指定。
  • 座標参照系(CRF)は、EPSG:4326 - WGS 84 あたりにしておいた。
  • 「レイヤメタデータを保持」のチェックを外す。
  • レイヤオプションのGEOMETRYを、「AS_WKT」にする。もし、ラインではなくポイントを出力したい場合は、「AS_XY」を選べば良いらしい。
  • 「保存されたファイルを地図に追加する」のチェックを外す。

qgis_ss_20240418_03.png


これで、座標値らしきもの(緯度と経度?)を、csvファイルとして保存することができた。以下は出力サンプル。

_road2.csv


csvの中では、MULTILINESTRING ((...)) という形で座標値が出力されているけれど、x,y値が、空白とカンマ区切りで並んでいるので、エディタで開いて置換をすれば、それらしいデータが得られる。

ただ、一筆書きのラインのように見えつつも、実は分断されていて、複数のラインで構成されてしまっている時がある。そういった状態の場合、出力された csv の中で、座標値が「),(」等で区切られてしまっていたりする。上記のデータも、見事に分断してるデータになっていた…。

できれば、QGIS上で、ちゃんと繋がったラインになるように修正したい。

スナップツールバーの表示 :

ラインの各頂点は、スナップ機能を使うことで、ピッタリ同じ座標にすることができる。そのため、修正作業の前準備として、ツールバー上にスナップツールバーも表示しておいたほうがいい。

ビュー → ツールバー → スナップツールバー、にチェックを入れる。これで、ツールバー上に、スナップツールバーが追加された状態になる。

qgis_ss_20240418_04.png

qgis_ss_20240418_05.png

磁石のようなアイコンをクリックすれば、スナップ機能の有効/無効を切り替えることができる。スナップ機能を有効にすれば、移動中の頂点を、他の頂点にピタリと吸着させることが ―― 移動中の頂点の座標値を、他の頂点の座標値とを全く同じ値にすることができる。

マーカーラインを使って最初の点と最後の点を表示 :

マーカーラインなる機能を使うと、「最初の点のみ」「最後の点のみ」に、何かしらのマーカーを表示することができるらしい。

_地物のラインに起点終点の表示をしたい

これを使えば、ラインが分断されていた場合、ラインのあちこちにマーカーが表示されるので、どこで分断されているか分かりやすいし、修正作業も楽になる。

ベクタレイヤ名の上で右クリックして「プロパティ」を選択。プロパティウインドウが表示される。
  • 左側で「シンボロジ」なるタブを選んで、マーカーラインシンボルレイヤを追加していく。
  • 右上のほうのプラスアイコンをクリックしてシンボルレイヤを追加。
  • シンボルレイヤタイプを「マーカーライン」にする。
  • マーカーの位置を、「最後の点のみ」にする。

qgis_ss_20240418_06.png


同様に、もう一つシンボルレイヤを追加して、マーカーの位置を「最初の点のみ」にする。

qgis_ss_20240418_07.png


それぞれ、マーカーの見た目を異なる色や形にしておいたほうが見分けやすい。

qgis_ss_20240418_08.png


これで、ラインが分断されているところには、マーカーがついてくれる。

qgis_ss_20240418_09.png


後は、編集モード切替、かつ、スナップ機能を有効にして、分断されている箇所の頂点が同じ位置になるように修正していく。

修正が終わったら、ベクタ → 空間演算ツール → 融合 (dissolve)、を選んで、繋がっているラインに変換。別レイヤとして追加される。

qgis_ss_20240418_10.png


さて、こうして得られた道路データは、正しいデータになっているのだろうか…。何かプログラムを作って、読み込ませて描画して、確認してみないと…。

Python+pygameで表示してみた :

Python 3.10.10 64bit + pygame 2.5.2 を使って、簡易表示してみることにした。

元のデータは以下。QGISからエクスポートしたcsvを、テキストエディタで修正して、x,y だけが並んでる状態にした。

_housakatouge.csv
_motosuko.csv

QGISのスクリーンショットと、Python + pygame で表示したソレを並べてみる。

draw_line_py_ss_001.png

draw_line_py_ss_002.png

draw_line_py_ss_004.png

draw_line_py_ss_005.png


一応それらしい形が表示されて喜んだものの、よく見てみると、なんだか縦に潰れてしまっている…。

元のデータはおそらく緯度と経度(経度と緯度?)なのだろうけど、1:1ではないのかもしれない。比率の違いがどの程度なのかググってみたけど、そのものズバリを説明してるページは見当たらず。しかし、たまたま、違いは1.3程度と言及してる箇所に遭遇。その値が本当に正しいのかどうかは分からんけれど、試しに縦方向を1.3倍してみた。

draw_line_py_ss_001.png

draw_line_py_ss_003.png

draw_line_py_ss_004.png

draw_line_py_ss_006.png


大体似た感じになってるから、これでいいかな…。本来、北や南に行くに従って、地球の円周の長さも変わってくるだろうから、そのあたりを補正しないといけない気もするけれど、正確な形状が欲しいわけでもないので、こんな感じでもいいだろう…。

書いた Pythonスクリプトは以下。

_draw_line.py

python draw_line.py housakatouge.csv
python draw_line.py motosuko.csv

カーソルキーの左右、もしくは上下で、カーソル位置を変更する機能もつけておいた。道路の形に沿ってカーソル位置が変わっていくことで、一筆書き状態のデータになっていることも確認できた。

余談。久々に pygame を触ったので使い方を色々忘れてしまっていて、ググって解説ページを眺めつつ作業してみたけれど。昔と違って、pygame の解説ページが随分と増えている印象を受けた。一時期の pygame がオワコン扱いライブラリだったことを思い返すと、なんだか目頭が熱く…。pygame で使ってる SDL が 1.x から 2.x になって描画処理が高速化したことで復権できたのだろうか。いや、tkinter (Tcl/Tk の Tk を Python から利用できるようにしたライブラリ)もそうだけど、Python の人気に引っ張られる形で各ライブラリ/モジュールが再注目されていった面もあるのだろうな…。

2024/04/19(金) [n年前の日記]

#1 [prog][python] スプライン曲線について調べている

_昨日、 QIGISを使って道路データを取り出せること、及び、取り出した道路データを Python + pygame で描画したらそれらしくなっていることを確認できた。

ただ、道路データは、連続している直線データとして取り出しているので、そのまま道路として描画したらカクカクした見た目になってしまうはず。何かしらの補間をかけて滑らかな見た目にしたい。ここはスプライン曲線とかそのあたりを使う場面かなあ、と…。

一応、昔、Ruby + DXRuby でその手の曲線を実装できるのか試したことがあるので、Python に移植してみてもいいのかもしれないけれど。

_mieki256's diary - 曲線について調べていたり
_mieki256's diary - スプライン曲線について調べてたり

プログラミング言語の中では大人気の Python なら、既にその手のライブラリがあるんじゃないか? そっちを使ったほうが早いんじゃないか? と思えてきて、関連情報をググっていた。

どうやら scipy というライブラリが使えそうだなと…。

_pythonでxy座標上の離散点をスプライン補間 #Python - Qiita
_スプライン曲線と補間 - Emotion Explorer
_scipy.interpolate.splprep - SciPy v1.13.0 Manual
_補間 - 読書ノート 1.6dev documentation

ただ、今まで触ったことがないので、何が何だか。とりあえずサンプルを動かすところから始めてみよう…。

scipy関連モジュールをインストール :

Windows10 x64 22H2 + Python 3.10.10 64bit上で、scipy、numpy、matplotlib をインストール。
pip install scipy
pip install numpy
pip install matplotlib
> pip list | egrep "scipy|numpy|matplotlib"
matplotlib                3.8.4
matplotlib-inline         0.1.6
numpy                     1.26.4
scipy                     1.13.0
  • scipy はスプライン補間に使う。
  • numpyは配列処理に使う。
  • matplotlib は得られたデータをグラフで描画するのに使う。

Debian Linux 11 bullseye i686でもインストールしてみた。Debian系の場合、パッケージが用意されているので、apt でインストールする。
sudo apt install python3-scipy python3-numpy python3-matplotlib
$ sudo apt list --installed | grep -E "scipy|numpy|matplotlib"

WARNING: apt does not have a stable CLI interface. Use with caution in scripts.

python-matplotlib-data/oldstable,now 3.3.4-1 all [インストール済み、自動]
python-scipy-doc/oldstable,now 1.6.0-2 all [インストール済み]
python3-matplotlib/oldstable,now 3.3.4-1 i386 [インストール済み]
python3-numpy/oldstable,now 1:1.19.5-1 i386 [インストール済み、自動]
python3-scipy/oldstable,now 1.6.0-2 i386 [インストール済み]

#2 [python][pygame] Debian Linux上でpygameをインストール

Linux上での動作確認をするために、pygame を、ネットブック機 Lenovo IdeaPad S10-2 + Debian Linux 11 bullseye i686上でインストールしてみた。

pygame というのは、Python で2Dゲームを作成できるライブラリ。

一般的に pygame は pip を使ってインストールすることが多いだろうけど、Debian Linux や Ubuntu Linux の場合、パッケージが用意されている。動作に必要な他のパッケージも一緒にインストールしてくれるので、そちらを使ってインストールしたほうがハマらない。

sudo apt install python3-pygame python3-pygame-sdl2 python-pygame-doc

pygame zeroについて :

pygameの記述を簡単にした、Pygame Zero というライブラリもあると知った。

_Pygame Zero へようこそ - Pygame Zero 1.2 ドキュメント

これもパッケージをインストールすることができる。
sudo apt install python3-pgzero

Pygame Zero を使う時は、python hoge.py ではなく、pgzrun hoge.py で実行する。もしくは、import pgzrun をファイルの最初のほうに書いておいて、最後のほうに pgzrun.go() と書いておいてもいいらしい。

_IDLE や 他の IDE での Pygame Zero 実行 - Pygame Zero 1.2 ドキュメント

Windows上では、pip でインストールできる。
pip install pgzero

pgzrun.exe は、以下に入っていた。
Pythonインストールフォルダ\Scripts\pgzrun.exe

2024/04/20() [n年前の日記]

#1 [prog][python] scipyでスプライン曲線を作成できるか実験

Python の scipy ライブラリを使うとスプライン曲線が作れるらしいと知り、そのあたりを実験中。

環境は Windows10 x64 22H2 + Python 3.10.10 64bit + scipy 1.13.0 + numpy 1.26.4 + matplotlib 3.8.4。

interpolate.splprep()が良さそう :

一般的に、scipy を使ってスプライン補間をする場合は、interpolate.interp1d() を使うようだけど、x の増分方向が同じ方向じゃないと使えないらしくて悩んでしまった。自分が処理したいデータは、平面上であっちに行ったりこっちに行ったりするデータなので…。

以下のページで、interpolate.splprep() ならそういうデータも処理できそうと知った。また、interpolate.interp1d() も、x と y を別々に処理すればどうにかできるらしい。

_pythonでxy座標上の離散点をスプライン補間 #Python - Qiita
_スプライン曲線と補間 - Emotion Explorer

上記のページのサンプルを参考にして、以下のデータを処理してみた。

_housakatouge.csv
_motosuko.csv

Pythonスクリプトは以下。

_draw_spline.py
import sys
import numpy as np
from scipy import interpolate
import matplotlib.pyplot as plt


# S = 10
S = 20


def loadData(infile):
    ax = []
    ay = []
    with open(infile, "r") as file:
        for line in file:
            line = line.rstrip()
            x, y = line.split(",")
            x = float(x)
            y = float(y)
            ax.append(x)
            ay.append(y)
    return ax, ay


# B-Spline 補間
def spline1(x, y):
    tck, u = interpolate.splprep([x, y], k=3, s=0)
    u = np.linspace(0, 1, num=len(x) * S, endpoint=True)
    spline = interpolate.splev(u, tck)
    return spline[0], spline[1]


# interp1dスプライン補間による曲線
def spline3(x, y):
    t = np.linspace(0, 1, len(x), endpoint=True)
    fx = interpolate.interp1d(t, x, kind="cubic")
    fy = interpolate.interp1d(t, y, kind="cubic")
    ta = np.linspace(0, 1, len(x) * S, endpoint=True)
    return fx(ta), fy(ta)


if len(sys.argv) < 2:
    sys.exit()

infile = sys.argv[1]
x, y = loadData(infile)

# B-Spline interpolation
bx, by = spline1(x, y)

# interp1d spline
ax, ay = spline3(x, y)

plt.plot(x, y, "ro", label="Data")
# plt.plot(bx, by, "b.", label="B-Spline interpolation")
plt.plot(bx, by, color="blue", label="B-Spline interpolation")
plt.plot(ax, ay, color="green", label="interp1d")

plt.legend(loc="best")
plt.savefig("output_graph.png")
plt.show()

python draw_spline.py motosuko.csv

draw_spline_py_ss_001.png

draw_spline_py_ss_002.png

なかなか興味深い(?)結果になった気がする。

まず、連続した直線データで構成されている道路データを読み込んで、スプライン曲線として描画することはできている。

ただ、interpolate.interp1d() で処理すると、不自然な曲線になってしまう箇所が、いくつも出てくるようだなと…。これが interpolate.splprep() なら、そこまでおかしな状態にはならない。これは後者を使って処理したほうが良さそうだなと…。

余談。matplotlib を使ってグラフを描画しているけれど、ツールバーに拡大ツールやPANツールがあって、これは便利だなと感心してしまった。
  • 虫眼鏡のアイコンをクリックして、キャンバス(?)上でマウスドラッグをして範囲を指定すると、その範囲が拡大表示される。
  • 上下左右の矢印っぽいアイコンをクリックすると、キャンバス上のマウスドラッグで表示位置を変更できる。
  • 家のアイコンをクリックすると、全体が収まる表示になる。

スプライン曲線化したデータを出力 :

スプライン曲線を描画することはできたけど、座標値として出力するにはどうしたらいいのか…。

以下のような感じだろうか。

_dump_spline.py
import argparse
import sys
import numpy as np
from scipy import interpolate


# load csv file
def loadData(infile):
    ax = []
    ay = []
    with open(infile, "r") as file:
        for line in file:
            line = line.rstrip()
            x, y = line.split(",")
            x = float(x)
            y = float(y)
            ax.append(x)
            ay.append(y)
    return ax, ay


# B-Spline
def spline1(x, y, s):
    tck, u = interpolate.splprep([x, y], k=3, s=0)
    u = np.linspace(0, 1, num=len(x) * s, endpoint=True)
    spline = interpolate.splev(u, tck)
    return spline[0], spline[1]


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("-i", "--input", required=True, help="Input csv file")
    parser.add_argument("-n", "--num", type=int, default=10, help="number")
    args = parser.parse_args()

    if args.input is None:
        sys.exit()

    x, y = loadData(args.input)
    bx, by = spline1(x, y, args.num)

    for i in range(len(bx)):
        print(f"{bx[i]},{by[i]}")


if __name__ == "__main__":
    main()

python -i dump_spline.py -n 10 motosuko.csv > motosuko_spline.csv

これで、スプライン曲線化したデータを取得することができた。

_housakatouge_spline.csv
_motosuko_spline.csv

しかし、このデータは、本当にスプライン曲線になっているのだろうか…? 表示して確認してみないと…。

matplotlibで表示確認 :

先日は Python + pygame でデータを描画して確認したけれど、matplotlib を使って描画したほうが、拡大表示や表示位置変更ができて便利だろうと思えてきた。

以下のような感じだろうか。

_draw_line_plot.py
import sys
import matplotlib.pyplot as plt


def loadData(infile):
    ax = []
    ay = []
    with open(infile, "r") as file:
        for line in file:
            line = line.rstrip()
            x, y = line.split(",")
            x = float(x)
            y = float(y)
            ax.append(x)
            ay.append(y)
    return ax, ay


if len(sys.argv) < 2:
    sys.exit()

infile = sys.argv[1]
x, y = loadData(infile)

plt.plot(x, y, color="cyan", label="Data (line)")
plt.plot(x, y, "r.", label="Data (point)")

plt.legend(loc="best")
# plt.savefig("output_graph.png")
plt.show()

python draw_line_plot.py motosuko_spline.csv

draw_line_plot_py_ss_001.png

draw_line_plot_py_ss_002.png

イイ感じかもしれない。

このデータを元にして、道路幅を加えて、ポリゴン描画すればそれらしくなるのではないかな…。

2024/04/21() [n年前の日記]

#1 [movie] 「ゴジラvsコング」を視聴

BS12で放送されていたので視聴。ゴジラとキングコング?が戦う映画らしい。2021年の映画。今月末に「ゴジラxコング 新たなる帝国」という新作映画が日本で公開されるので、その宣伝を兼ねて、ということだろうか。

全然期待してなかったのだけど、かなり面白かった。何より迫力のある映像が目白押しで、そこだけでも楽しめる印象。

小栗旬さんが出演していたのだけど、 ヘブン状態のカットで思わず笑ってしまった。あのカットだけでも、この映画を見ておいて良かったなあ、元が取れたぞ、と思えたぐらいにとても良い表情だった…。まあ、あのタイミングであんなカットが出てきたら不意打ちに近いよな…。

なんとなくだけど、コングさん側に結構感情移入してしまった。いやまあ、トカゲと猿なら、そりゃ猿のほうに親近感を持つのが自然だろうと思うけど。それに、コングさんって基本的にはゴジラより弱いし。いわゆる判官贔屓だろうか。「負けるな、コングさん」と思いながら見れたので、これは良い怪獣バトル映画なのではないかと。

2024/04/22(月) [n年前の日記]

#1 [python] PyOpenGLでモデルデータを描画したい

Python + PyOpenGL + glfw で、モデルデータを描画したい。とりあえず、wavefront obj形式のモデルデータを読み込んで描画できるだけでも助かる。

環境は Windows10 x64 22H2 + Python 3.10.10 64bit。PyOpenGL 3.1.6 + PyOpenGL-accelerate 3.1.6 + glfw 2.7.0。

ライブラリを探す :

wavefront obj形式を解析する処理を自分で書いてもいいのだけど、大人気のプログラミング言語、Python のことだから、絶対に誰かが既にそういう処理を書いてライブラリにしているはず。PyPI で検索してみた。

_PyPI - The Python Package Index

色々見かけたけど、PyWavefront とやらはどうだろう。

_PyWavefront - PyPI
_pywavefront/PyWavefront: Python library for importing Wavefront .obj files
_【Pythonライブラリ】「pywavefront」のサンプルコード | YuNi-Wiki

pip でインストール。
pip install pywavefront

PyWavefront 1.3.3 がインストールされた。

PyWavefrontの使い方 :

とりあえず、以下のように書けば、.obj と .mtl を読み込んでくれるらしい。
import pywavefront

obj = pywavefront.Wavefront("hoge.obj")

ただ、変数objに入った、このクラスが、どんな情報を持っているのかが分からない…。とりあえず、それらしい情報を print() で出力してみよう…。

テストに使ったモデルデータは以下。

_cube01.obj
_cube01.mtl
_suzanne01.obj
_suzanne01.mtl
_car.obj
_car.mtl

car.obj、car.mtl は、以下のデータを利用させてもらった。ライセンスがCC0なので、改変して利用してもOK。自由に使える。ありがたや。ほんの少しだけ修正して、ポリゴン数を微妙に減らして使ってみた。

_Car Kit | OpenGameArt.org
_Car Kit - Kenney

テストに使った Pythonスクリプトは以下。

_dump_obj.py
import pywavefront

infile = "cube01.obj"
# infile = "suzanne01.obj"


def dump_material(mat):
    print()
    print(f".name = {mat.name}")
    print(f".vertex_format = {mat.vertex_format}")
    print(f".vertices = {mat.vertices}")
    print(f".diffuse = {mat.diffuse}")
    print(f".ambient = {mat.ambient}")
    print(f".specular = {mat.specular}")
    print(f".texture = {mat.texture}")


def main():
    obj = pywavefront.Wavefront(infile)
    # obj.parse()

    print(f"file_name = {obj.file_name}")
    print(f"mrllibs = {obj.mtllibs}")
    print(f"vertices = {obj.vertices}")

    print(f"\nmaterials = {obj.materials}")
    # for matname, mat in obj.materials.items():
    #     dump_material(mat)

    print(f"\nmeshes = {obj.meshes}")
    # for meshname, mesh in obj.meshes.items():
    #     print()
    #     print(f"Mesh name = {meshname}")
    #     print(f"name = {mesh.name}")
    #     print(f"faces = {mesh.faces}")
    #     print(f"materials = {mesh.materials}")
    #     print("\n# mesh.materials")
    #     for mat in mesh.materials:
    #         dump_material(mat)

    print(f"\nmesh_List = {obj.mesh_list}")
    for mesh in obj.mesh_list:
        print(f".name = {mesh.name}")
        print(f".faces = {mesh.faces}")
        print(f".materials = {mesh.materials}")
        for mat in mesh.materials:
            dump_material(mat)


if __name__ == "__main__":
    main()

python dump_obj.py

出力結果は以下。

_dump_obj_output.txt

どうやら、.mesh_list が持ってる情報を使えば、描画ができそうだなと…。
  • .mesh_list という配列の中に、各mesh が入っている。
  • 各meshは、マテリアル(.materials) を持っている。
  • そのマテリアルの中に、頂点配列(.vertices)も含まれている。
  • 頂点配列は、テクスチャUV情報、法線情報、頂点座標値が、インターリーブされた形で入ってる。
  • どんなインターリーブになっているかは、.vertex_format に入っている。

インターリーブについて :

OpenGLのインターリーブについては、以下のページが参考になるかなと…。

_GPU本来の性能を引き出すWebGL頂点データ作成法 #WebGL - Qiita
_wgld.org | WebGL: インターリーブ配列 VBO |

要は、頂点座標、法線情報、テクスチャUV情報などを、頂点1つ毎にチマチマと並べている頂点配列の格納形式、ということでいいのだろうか。ただ、上記のページの解説によると、昔のGPUではインターリーブをすると高速化できたけど、今のGPUでは遅くなるそうで…。

そのインターリーブとやらは OpenGL 1.1 でも使えるのだろうかと気になったけど、Microsoftのページでは、「OpenGL 1.1以降で使える」と書いてあるようだなと…。

_glInterleavedArrays 関数 (Gl.h) - Win32 apps | Microsoft Learn
注意 : glInterleavedArrays 関数は、OpenGL バージョン 1.1 以降でのみ使用できます。

glInterleavedArrays 関数 (Gl.h) - Win32 apps | Microsoft Learn より

PyWavefrontを利用して描画してみる :

PyOpenGL 3.1.6 + glfw 2.7.0 + PyWavefront 1.3.3 を利用して、wavefront obj形式のモデルデータを読み込んで描画してみる。

以下のような見た目になった。




_draw_opengl_glfw.py
import glfw
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
import pywavefront

model_kind = 2

modeldata = [
    {"file": "./cube01.obj", "scale": 2.0},
    {"file": "./suzanne01.obj", "scale": 5.0},
    {"file": "./car.obj", "scale": 7.0},
]

SCRW, SCRH = 1280, 720
WDWTITLE = "Draw wavefront obj"
FOV = 50.0

light_pos = [1.0, 1.0, 1.0, 0.0]
light_ambient = [0.2, 0.2, 0.2, 1.0]
light_diffuse = [0.9, 0.9, 0.9, 1.0]
light_specular = [0.5, 0.5, 0.5, 1.0]

winw, winh = SCRW, SCRH
ang = 0.0
obj = None


def init_animation(infile):
    global ang, obj
    ang = 0.0
    obj = pywavefront.Wavefront(infile)


def render():
    global ang, obj
    ang += 45.0 / 60.0

    # init OpenGL
    glViewport(0, 0, winw, winh)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(FOV, float(winw) / float(winh), 1.0, 1000.0)

    # clear screen
    glClearDepth(1.0)
    glClearColor(0, 0, 0, 1)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()

    glDepthFunc(GL_LESS)
    glEnable(GL_DEPTH_TEST)
    glEnable(GL_BLEND)
    glEnable(GL_NORMALIZE)

    glEnable(GL_CULL_FACE)
    glFrontFace(GL_CCW)
    # glCullFace(GL_FRONT)
    glCullFace(GL_BACK)

    # set material
    glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE)
    glEnable(GL_COLOR_MATERIAL)

    # set lighting
    glLightfv(GL_LIGHT0, GL_POSITION, light_pos)
    glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient)
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse)
    glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular)
    glEnable(GL_LIGHTING)
    glEnable(GL_LIGHT0)

    # obj move and rotate
    glTranslatef(0.0, 0.0, -20.0)
    scale = modeldata[model_kind]["scale"]
    glScalef(scale, scale, scale)
    glRotatef(20.0, 1, 0, 0)
    # glRotatef(ang * 0.5, 1, 0, 0)
    glRotatef(ang, 0, 1, 0)

    # draw obj
    for mesh in obj.mesh_list:
        for mat in mesh.materials:
            r, g, b, a = mat.diffuse
            glColor4f(r, g, b, a)
            gl_floats = (GLfloat * len(mat.vertices))(*mat.vertices)
            count = len(mat.vertices) / mat.vertex_size
            glInterleavedArrays(GL_T2F_N3F_V3F, 0, gl_floats)
            glDrawArrays(GL_TRIANGLES, 0, int(count))


def key_callback(window, key, scancode, action, mods):
    if action == glfw.PRESS:
        if key == glfw.KEY_ESCAPE or key == glfw.KEY_Q:
            glfw.set_window_should_close(window, True)


def resize(window, w, h):
    if h == 0:
        return
    set_view(w, h)


def set_view(w, h):
    global winw, winh
    winw, winh = w, h
    glViewport(0, 0, w, h)


def main():
    if not glfw.init():
        raise RuntimeError("Could not initialize GLFW3")
        return

    window = glfw.create_window(SCRW, SCRH, WDWTITLE, None, None)
    if not window:
        glfw.terminate()
        raise RuntimeError("Could not create an window")
        return

    glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 1)
    glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 1)
    glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
    glfw.window_hint(glfw.DEPTH_BITS, 24)

    glfw.set_key_callback(window, key_callback)
    glfw.set_window_size_callback(window, resize)
    glfw.make_context_current(window)
    glfw.swap_interval(1)

    set_view(SCRW, SCRH)

    # get csv filepath
    infile = modeldata[model_kind]["file"] if len(sys.argv) != 2 else sys.argv[1]
    init_animation(infile)

    # main loop
    while not glfw.window_should_close(window):
        render()
        glfw.swap_buffers(window)
        glfw.poll_events()

    glfw.destroy_window(window)
    glfw.terminate()


if __name__ == "__main__":
    main()


render() の中で OpenGL関係の描画をしているから、そこを見れば大体分かりそうだけど。肝は以下だろうか…。
import pywavefront

# ...

    obj = pywavefront.Wavefront(infile)

# ...

    # draw obj
    for mesh in obj.mesh_list:
        for mat in mesh.materials:
            r, g, b, a = mat.diffuse
            glColor4f(r, g, b, a)
            gl_floats = (GLfloat * len(mat.vertices))(*mat.vertices)
            count = len(mat.vertices) / mat.vertex_size
            glInterleavedArrays(GL_T2F_N3F_V3F, 0, gl_floats)
            glDrawArrays(GL_TRIANGLES, 0, int(count))

PyWavefront を使って読み込むと、マテリアル毎に、そのマテリアル情報を使って描画したい頂点配列をまとめてくれるので…。
  • マテリアル単位でループ処理する。
  • マテリアル情報を元にしながら色の設定を行う。
  • glInterleavedArrays() でインターリーブのフォーマットを指定。
  • glDrawArrays() で頂点配列の描画を指示。

インターリーブのフォーマットについては、今回のテストモデルデータを使う限り、GL_T2F_N3F_V3F を固定で指定しちゃって問題無さそうだったけど。本来は、.vertex_format から指定すべき値を決めないといけない気がする。

ちなみに、GL_T2F_N3F_V3F は、テクスチャUVがfloatで2個分(=T2F)、法線情報がfloatで3個分(=N3F)、頂点座標がfloatで3個分(=V3F)、の順番で入っていると示す値、だと思う。

さておき。描画されたモデルの色が、なんだかおかしい気がする…。blender上で表示されたソレとは随分違う色になってしまっていて…。OpenGL側でマテリアルの指定をするあたりで何か間違えてしまっている気がする。

#2 [prog] 平方根を使ったほうが速いらしい

思考メモ。

とあるPythonスクリプトを書いてる際、途中で平方根を求める部分が出てきて、特に何も考えずに sqrt() を使って書いてしまったのだけど。ふと、「平方根を求めるところで遅くなったりしないのかなあ?」と不安になった。

気になったので少しググってみたら…。今時のCPUは、平方根を求めるための専用のCPU命令が用意されていたりするから、実は素直に平方根を使って計算しちゃったほうが一番速かったりする、と知った。えー。そんなことになってたのか…。今頃になって結構ビックリ。いやまあ、使ってるCPUにもよるだろうし、コンパイラによっても違ってくるだろうし、各プログラミング言語の sqrt() がどういう実装をしているかも絡んでくるのだろうけど。

OpenGLのインターリーブの件もそうだろうけど、ハードウェアの仕組みが変わったことで、昔の高速化手法を使うとかえって遅くなってしまう場面があちこちにあるのかもしれないなと…。

2024/04/23(火) [n年前の日記]

#1 [comic] 「ダンジョン飯」1-11巻を読んだ

弟が帰省した際に置いて行ってくれた漫画単行本を少しずつ消化。「ダンジョン飯」を読み始めたのだけど、数巻ほど読めればいいかなと思ってたのに、面白くてついつい先へ先へと…。

アニメ版と比べると、原作の初期の頃は結構線が少ない感じなのだなと再認識。もちろん小さいコマの中にみっちり描き込んでも読み辛くなるだけなので…。イラストじゃなくて漫画なのだから、とにかく描き込んであればそれでいいというわけでもないのだよな…。その点この作品は絶妙なバランスで仕上げてあるように見えて好印象。

2024/04/24(水) [n年前の日記]

#1 [comic] 「ダンジョン飯」12-14巻を読んだ

あまりに面白くて、とうとう最終巻まで読んでしまった…。色んな面でタイトルに偽り無し、だった気がする…。しかしよくまあこんな設定を思いつくもんだなと…。着眼点も、思考の方向性も、非凡極まりない…。それはともかく、これって最後までアニメ化できるんだろうか…。

2024/04/25(木) [n年前の日記]

#1 [python][prog] OpenGLで道路を描画するPythonスクリプトを書いてる

OpenGLを使ってアイソメトリック的な見た目で道路を描画するPythonスクリプトを書いている。道路をポリゴンで描く処理もできたし、車のモデルデータを描画して、道路の向きに合わせて回転させる処理もできた。

道路のカーブがところどころ不自然なので、道路データを修正。QGIS を起動して、道路データを ―― 連続した直線データの頂点位置を変更して、再度エクスポート。元々交差点だった地点では90度で曲がっていて、それをそのまま残していたからおかしくなっていた模様。ただ、理想としては、交差点等もそのまま画面に出したい気も…。もしかすると、描画用のデータと、車を移動させるデータを、別々に持ってしまったほうがいいのだろうか…。しかしその場合、描画データをどう持てばいいのか…。

カーブに差し掛かったら車の移動速度を落とすようにしたい。現在の道路セグメントインデックス値から、前方に、何セグメント分か見て、角度の差の合計を求めて、カーブがあるかどうかを判定するようにしてみた。しかし、どうもイイ感じになってくれない。減速しなくていい場所で減速したり、減速してほしい場所で減速しなかったり、急加速が目立ったり…。もしかすると道路データに、ここからここまで減速、的な情報を手作業で追加したほうがいいのだろうか。

2024/04/26(金) [n年前の日記]

#1 [python][prog] OpenGLで道路を描画するPythonスクリプトを書いてる。その2

OpenGLを使ってアイソメトリック的な見た目で道路を描画するPythonスクリプトを書いている。

道路は描画できたので、その周辺に、木だか山だかを感じさせる緑色の三角形をランダムに散りばめたいと思ったのだけど、1000枚ぐらい乱数で配置して描画してみたら処理落ちしてしまった…。描画枚数を減らさないと厳しい…。

考えてみたら、道路から遠く離れた場所に配置してしまって無駄になってる場合もありそうだなと気づいた。とりあえず、道路のセグメントデータに対応する形で、道路の脇に配置するようにしつつ、そのフレームで描画してる道路のセグメント数と同じ数だけ三角形を描画するようにしてみたところ処理落ちしない状態になった。

それはさておき。三角形が道路と重なる位置に配置されてしまう時があって、どう解決したらいいものかと…。

ひとまず、道路のセグメントの中心位置と道路幅、三角形の中心位置と三角形の大きさで、2つの円があるものとして衝突判定をして、ぶつかってる三角形は無効にするようにしてみた。

hitcheck_ss.png

これである程度、道路と重なった三角形を排除できたけど、スクリプト実行開始時の初期化処理、衝突判定をするところで、何秒もかかってしまう。三角形を一つ配置するたびに、道路とぶつかってないか、道路のセグメントデータと総当たりで衝突判定してるけど…。セグメントデータが4000以上あるので、配置する三角形の数 x 4000回以上の衝突判定になってしまう。そんなことをしていたら時間がかかってしまうのは当たり前。何かいい手を考えないと…。

2024/04/27() [n年前の日記]

#1 [python][prog] OpenGLで道路を描画するPythonスクリプトを書いてる。その3

OpenGLを使ってアイソメトリック的な見た目で道路を描画するPythonスクリプトを書いている。

大体出来たような気がするので、github にアップロードしてみた。道路データが記述されたcsvファイルを読み込んで、OpenGL (PyOpenGL + glfw)で描画している。

_mieki256/drawroadiso_py: Drawing roads with isometrics using Python and OpenGL

初期化に時間がかかっていた件 :

道路と周辺の三角形が重ならないように、初期化処理時に衝突判定をして対処するようにしてみたところ、処理時間がかかってしまって悩んでいたけれど。別のスクリプトを用意して、描画に必要になる道路データを ―― 道路と三角形が重ならないように処理済みのデータを、あらかじめ事前に作成しておいて、描画するスクリプトは用意済みのデータを読み込んでそのまま描画するだけ、という仕組みにすることでどうにか解決してみた。何もわざわざ、スクリプト実行時に、その都度処理をしなくてもいいよなと…。

ただ、どうしたら衝突判定の回数を少なくできるのか、そのあたりの方法には興味がある。今後の宿題。絶対に何かしらの方法があるはず…。

描画が遅いかもしれない :

メインPC、AMD Ryzen 5 5600X + NVIDIA GeForce GTX 1060 6GB なら、一応60FPSで動いてるように見えているけれど。少し非力な環境で動かしたら処理落ちしそうだなと…。

原因は、毎フレーム、512 + 512枚ほど道路のポリゴン + 三角形ポリゴンを、glBegin() - glEnd() で描いてることかなと…。スクリーン外のポリゴンも描画対象にしてしまって無駄な処理をしているだろうし、glBegin() - glEnd() が遅かったりしそうだし…。ポリゴンをある程度まとめておいて、ディスプレイリストなり、頂点配列なりで描画すれば状況が違ってくるかもしれない…?

ただ、そのためには、全体のエリアを分割して複数の小エリアにして並べないといかんかなと…。それぞれの小エリアが固定された道路のモデルデータを持つようにして、各フレームではどの小エリアを描画するかを決定して、みたいな処理をしないといけない気がする。その場合、事前にPythonスクリプトで、小エリア単位のデータに変換して出力しておく、みたいな感じになるのかなと…。

2024/04/28() [n年前の日記]

#1 [prog] OpenGLで道路を描画するPythonスクリプトをC言語で書き直し

_昨日、 OpenGLを使って、アイソメトリック的な見た目で道路を描画するPythonスクリプトを書いたけど、これをC/C++で書き直そうとしているところ。C/C++で書ければ、スクリーンセーバにもできるかなと…。

まずは、道路データ用のcsvファイルをC言語の配列の形にして、実行ファイルに組み込むようにしたい。道路データをcsv出力するPythonスクリプトを修正中。

当たり判定関係のアルゴリズムも調べてる :

4000以上ある道路セグメントデータと、木に相当する三角ポリゴンを衝突判定させると遅すぎるので、そのあたりを改善したい。何か手はないものかとググってみたところ、一般的には四分木空間分割なる手法が使われるっぽいなと…。

_四分木空間分割とは #アルゴリズム - Qiita
_その8 4分木空間分割を最適化する!(理屈編)

そのうち試してみよう…。

#2 [nitijyou] 部屋の温度が高い

温度計を見たら30度になってた。まだ5月にもなってないのに…。

2024/04/29(月) [n年前の日記]

#1 [prog] OpenGLで道路を描画するPythonスクリプトをC言語で書き直し。その2

_先日、 OpenGLを使って、アイソメトリック的な見た目で道路を描画するPythonスクリプトを書いたけど、これをC/C++で書き直そうとしているところ。

道路を描画してみたらガクガクした見た目になって悩んでしまった。原因は精度。Python版は道路データをfloatで持っているけど、PythonのfloatはC言語のdouble相当だそうで…。C言語版で各種座標値をfloatではなくdoubleにしてみたら、Python版と同じ見た目になってくれた。

ある程度それっぽく動くようになってきたけど、道路データの種類が2種類しかないのは寂しい。もうちょっと増やしたい。でも、どのあたりの道路がいいだろうか…。

2024/04/30(火) [n年前の日記]

#1 [prog] OpenGLで道路を描画するPythonスクリプトをC言語で書き直し。その3

_先日、 OpenGLを使って、アイソメトリック的な見た目で道路を描画するPythonスクリプトを書いたけど、これをC/C++で書き直そうとしているところ。

道路データを増やした :

QGIS Desktop 3.22.10 を使って、道路データを2つほど増やしてみた。
  • 磐梯吾妻スカイライン … 福島県の磐梯山近くの道路。昔は有料の道路だったけど今は無料で走れるようになったらしい。ただし冬は封鎖される。
  • 夜叉神峠までの道 … ゆるキャンアニメ版1期でしまりんが通ろうとしたけど冬は通行止めになっててガックリしていた場所。そこまでへの道を取り出してみた。

ちなみに今まで使ってた道路データは以下。
  • しまりん実家から本栖湖キャンプ場までの道 … ゆるキャンアニメ版1話でしまりんが自転車で走ったのであろう道。めっちゃ遠いし坂がキツイらしい。しまりんは鉄人。
  • 鳳坂峠(ほうさかとうげ) … 福島県の道路。バイパスが出来て、今は出入口を封鎖されてるようなので、今では幻の峠道。

#2 [prog][linux] テキストファイルを行単位で逆順にしたい

道路データが列挙されたcsvファイルの上下を逆にしたい。開始点と終了点を逆に入れ替えたいわけで…。

ググってみたら、*NIXの場合は tac というコマンドが使えると知った。

_【 tac 】コマンド/【 rev 】コマンド――ファイルを逆順に出力する:Linux基本コマンドTips(111) - @IT

MinGW/MSYS を有効にした状態、もしくは MSYS2上なら使える。
tac hoge.txt > hoge_rev.txt

DOSコマンドではそういうのって無いのかな…。あるいは、テキストエディタ xyzzy でもそういう機能があればいいのだけど…。

GNU utilities for Win32を使えそう :

*NIX関係のツールをWindows用に移植した、GNU utilities for Win32 (UnxUtils.zip, UnxUpdates.zip) を利用する手もありそう。

_Native Win32 ports of some GNU utilities

UnxUpdates.zip を解凍すると、中に tac.exe が入っている。

busyboxも使えそう :

busybox を使ってしまうのもアリだろうか。busybox は、*NIXでよく使うコマンドを一つにまとめてしまったツール。

_busybox-w32

busybox.exe をDLしてパスが通ったどこかに置いておけば、以下のような感じで使える。
busybox tac hoge.txt

#3 [anime] ゆるキャンの聖地関係をググってる

自作のデモプログラムに道路データを追加するにあたって、良さそうな場所がないものか、せっかくだからゆるキャン絡みの道路がいいかもしれないなとググっているのだけど…。ゆるキャンの聖地巡礼って凄いことになってるのだな…。ちょっとググっただけで、膨大な数の紹介ページ/案内ページが出てくる…。アニメ本編内で各地をしっかり描写していたからこそ、こういう展開もできるのだろうなと感心してしまった。もっともその分、アニメ版を作るのは大変だっただろうなと改めて思えてきたりもして。

ただ、どれも観光名所をピンポイントで紹介してるページばかりで、キャラ達が通ったであろう道路の形が分かるものには遭遇せず。まあ、聖地巡礼をしたいだけなら、どのルートを通ってその場所に到達してもいいわけで、道自体の情報は出てこなくても当然かもしれない。

それにしても…ゆるキャン凄いな…。ここまでとは…。アニメ版もちゃんと作って、聖地の側もちゃんと宣伝して…。偉いな…。

以前、仙台が舞台のアニメがあったのに地上波放送もしなかったし地元も作品の存在をガン無視、という話を聞いた記憶もあるわけで…。そういう事例と比べると、ゆるキャンは色々とちゃんとしてる…。もっとも、例えば東京を舞台にしたアニメが放送されたら東京でその手の宣伝をするのかと言えばおそらく一切しないわけで、仙台もそういう感覚なのかもしれない。都会だもんな。仙台。

以上、30 日分です。

過去ログ表示

Prev - 2024/04 - Next
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

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project