|
 |
|
2021/10/30(Sat) 12:14:21|NO.94254
●HGIMG4で基点を原点にした拡大縮小、及び回転を実装したい
HGIMG4で基点を原点にしたノード(オブジェクト)の拡大縮小、及び回転を実装したいです。
イメージとしては、blenderのトランスフォームピボットポイントの
「3Dカーソルを中心に操作する」トランスフォームモードです。
HGIMG4の拡大縮小や回転は「それぞれの原点」を基点とした拡大縮小や回転だと思いますが、
基点を原点にした(基点の位置を変えられる)拡大縮小、及び回転が使えれば便利だと思いました。
基点を原点にした拡大縮小命令と回転命令のイメージ
/*
通常の拡大と、基点からの拡大の違い
3つの頂点がある2mの線のオブジェクトを例えとする。
拡大元のスケール(左の頂点の位置,中心の頂点の位置,右の頂点の位置)
-1.0, 0.0, 1.0
通常の拡大後(setscale 2.0)
-2.0, 0.0, 2.0
基点からの拡大
基点が-1.0にあった場合のscale 2.0
-1.0, 1.0, 3.0
横のスケールと位置の関係について
スケールX倍率 1.0 の場合 -> 位置変わらず、スケールも変わらず
スケールX倍率 2.0 の場合 -> 位置は変化する、スケールは2倍
これをどうやってHGIMG4で実装したらいいのだろうか?
*/
#module
#include "hspmath.as"
; 3次元の距離を取得する関数
; 対象のノード間の距離を取得する
; この関数も正確かどうかは自信がない
; u_id0 = ノードID
; u_id1 = ノードID
#defcfunc dist3node var u_id0, var u_id1
getpos u_id0, x0, y0, z0
getpos u_id1, x1, y1, z1
xy0 = x0, y0
xy1 = x1, y1
yz0 = y0, z0
yz1 = y1, z1
zx0 = z0, x0
zx1 = z1, x1
return ( distance2(xy0,xy1) + distance2(yz0,yz1) + distance2(zx0,zx1) ) * 0.5
; 基点からのsetscale命令をイメージした仮の命令
; u_objID = スケール及びポジションを変更する対象のノード
; u_pivID = 基点のノード
; u_w = u_objIDの寸法の横幅
; u_h = u_objIDの寸法の縦幅
; u_d = u_objIDの寸法の奥行幅
; u_x = スケールX倍率
; u_y = スケールY倍率
; u_z = スケールZ倍率
#deffunc pivOriginSetScale var u_objID, var u_pivID, double u_w, double u_h, double u_d, double u_x, double u_y, double u_z
; pivIDのposを取得
; これが基点になる
getpos u_pivID, px, py, pz
; 対象のノードのposを取得
getpos u_objID, tx, ty, tz
w = u_w * u_x ; 実際の寸法の横幅(ケージ)
h = u_h * u_y ; 実際の寸法の縦幅(ケージ)
d = u_d * u_z ; 実際の寸法の奥行幅(ケージ)
; pivIDのposを原点にu_objIDのposを拡大分ズラす?
; ズラすためには、スケールの倍率の取得だけではなく、実際のオブジェクトの寸法の入力が必要?
; もしくは距離も使用する?
; (setposかaddposを使った何らかの処理?)
; スケールの拡大率は通常と同じ?
setscale u_id, u_x, u_y, u_z
return
; 基点からのsetang命令をイメージした仮の命令
; u_objID = スケール及びポジションを変更する対象のノード
; u_pivID = 基点のノード
; u_x = 回転X倍率
; u_y = 回転Y倍率
; u_z = 回転Z倍率
#deffunc pivOriginSetRotate var u_objID, var u_pivID, double u_x, double u_y, double u_z
; pivIDのposとangを取得
; これが基点になる
getpos u_pivID, pPosX, pPosY, pPosZ
getang u_pivID, pAngX, pAngY, pAngZ
; 対象のノードのposを取得
getpos u_objID, tPosX, tPosY, tPosZ
getang u_objID, tAngX, tAngY, tAngZ
; 位置をcos()やsin()を角度で計算し、移動した上で、
; 対象のノードのangをその回転分、回転させる?
; (sin()やcos()や距離取得関数を使った何らかの処理?)
; 処理の結果を設定する
setpos u_objID, result_posX, result_posY, result_posZ
setangZ u_objID, result_angX, result_angY, result_angZ
return
#global
おそらく、基点からの拡大縮小はスケールの拡大率は通常と同じで、
基点の位置と関係したなんらかのポジションの変更の処理が必要なのではないかと思いました。
基点からの回転の方は位置をcos()やsin()を角度で計算し、移動した上で、
対象のノードのangをその回転分、回転させるのだと思いました。
これらを、実際にスクリプトにしようとしてみると難しくて、
どうやればいいのかよく分かりませんでした。
距離関数については使うかどうか分からなかったのですが、
とりあえずdistance2の3次元版のようなものも添付したのですが、
これ自体も正しいアルゴリズムなのかどうかは自信がありません。
ぜひアドバイスお願い致します。

| |
|
2021/10/30(Sat) 19:16:43|NO.94259
こんなイメージでしょうか。
※quaternion積ができたので再投稿。
#include "hgimg4.as"
#module
#deffunc _fvqmul array _dst, double _qx, double _qy, double _qz, double _qw, local _re, local _cv
// _dst: 左の quaternion かつ結果を格納する
// _qx, _qy, _qz, _qw: 右の quaternion
_re = _dst.3 * _qw - _dst.0 * _qx - _dst.1 * _qy - _dst.2 * _qz
ddim _cv, 3
fvset _cv, _dst.0, _dst.1, _dst.2
fvouter _cv, _qx, _qy, _qz
fvadd _cv, _dst.0 * _qw + _qx * _dst.3, _dst.1 * _qw + _qy * _dst.3, _dst.2 * _qw + _qz * _dst.3
_dst.0 = _cv.0
_dst.1 = _cv.1
_dst.2 = _cv.2
_dst.3 = _re
return
#deffunc _rotxyz array _result, array _src, double _rx, double _ry, double _rz, local _cs, local _sn, local _tv2, local _tv3
// _result: 結果を書き出す先の 3要素のdouble配列
// _src: 回転される方向ベクトル 3要素のdouble配列
// _rx, _ry, _rz: getang と一致するオイラー回転 [rad]
_cs = cos(_rx) // X回転
_sn = sin(_rx)
ddim _tv2, 3
_tv2.0 = _src.0
_tv2.1 = _src.1 * _cs - _src.2 * _sn
_tv2.2 = _src.1 * _sn + _src.2 * _cs
_cs = cos(_ry) // Y回転
_sn = sin(_ry)
ddim _tv3, 3
_tv3.0 = _tv2.0 * _cs + _tv2.2 * _sn
_tv3.1 = _tv2.1
_tv3.2 = - _tv2.0 * _sn + _tv2.2 * _cs
_cs = cos(_rz) // Z回転
_sn = sin(_rz)
_result.0 = _tv3.0 * _cs - _tv3.1 * _sn
_result.1 = _tv3.0 * _sn + _tv3.1 * _cs
_result.2 = _tv3.2
return
#deffunc pivOriginSetRotate int u_objID, int u_pivID, double u_x, double u_y, double u_z
; pivIDのposとangを取得
; これが基点になる
getpos u_pivID, pPosX, pPosY, pPosZ
getang u_pivID, pAngX, pAngY, pAngZ
getquat u_pivID, pqx, pqy, pqz, pqw
; 対象のノードのposを取得
getpos u_objID, tPosX, tPosY, tPosZ
getang u_objID, tAngX, tAngY, tAngZ
getquat u_objID, tqx, tqy, tqz, tqw
ddim tv, 3
fvset tv, tPosX, tPosY, tPosZ
fvsub tv, pPosX, pPosY, pPosZ // 基点の分引く
ddim v, 3
_rotxyz v, tv, pAngX, pAngY, pAngZ // 回転する
fvmul v, u_x, u_y, u_z // スケール反映する
fvadd v, pPosX, pPosY, pPosZ // 基点の分足して戻す
result_posX = v.0
result_posY = v.1
result_posZ = v.2
// quaternion 積
ddim q, 4
q.0 = pqx
q.1 = pqy
q.2 = pqz
q.3 = pqw
_fvqmul q, tqx, tqy, tqz, tqw
; 処理の結果を設定する
setpos u_objID, result_posX, result_posY, result_posZ
setquat u_objID, q.0, q.1, q.2, q.3
return
#global
randomize
gpreset
setcls CLSMODE_SOLID, 0x404040
setpos GPOBJ_CAMERA, 1,1,12
gplookat GPOBJ_CAMERA, 0,0,0
gpfloor floorid, 10,10, 0x333333 // zx 平面
gpbox id, 0.1, 0xffffff // 原点
dim ids, 3
gpbox idleft, 0.2, 0xff0000
setpos idleft, -1.0, 0.0, 0
setang idleft, 0,0,0
gpbox idcenter, 0.2, 0x00ff00
setpos idcenter, 0,0,0
setang idcenter, 0,0, M_PI * 45.0 / 180.0
gpload idright, "res/tamane2"
setscale idright, 0.01, 0.01, 0.01
setpos idright, 1,0,0
setang idright, 0, M_PI * double(rnd(360)) / 180.0, 0
ids.0 = idleft
ids.1 = idcenter
ids.2 = idright
gpbox pivid, 0.5, 0xffffff // 基点と回転量のためのオブジェクト
setpos pivid, -1,0,0
setang pivid, 0,0, M_PI * double(rnd(90)) / 180
setalpha pivid, 32
repeat 3
pivOriginSetRotate ids.cnt, pivid, 2,2,2
loop // pivid に対して回転と拡大移動を適用
repeat
redraw 0
gpdraw
redraw 1
await 1000/60
loop

| |
|
2021/10/31(Sun) 16:07:53|NO.94268
アドバイスありがとうございます。
大変参考になりました。
回転精度についてですが、
; 処理の結果を設定する
setpos u_objID, result_posX, result_posY, result_posZ
setangZ u_objID, result_angX, result_angY, result_angZ ; setangからsetangZに変更
こうするとイメージ通りの回転になると思いました。
この時重要なのは、
; 確認のため、値はすこし変えてあります
gpbox pivid, 1, 0xffffff // 基点と回転量のためのオブジェクト
setpos pivid, 0.0, 0.0, 0.0
setangZ pivid, -m_pi * 0.25, 0.0, -m_pi * 0.25 ; setangからsetangZに変更
setalpha pivid, 32
このように、基点となるpivid側のangもsetangZに変更することでした。
大変勉強になりました。
ありがとうございました!
|
|