アプリ版:「スタンプのみでお礼する」機能のリリースについて

Java3Dで円筒をY軸方向に直立配置した後、Y軸を中心軸としての回転アニメーションをさせるところまでは問題ないのですが、この円筒を90度倒して、すなわち、横置きにして同じ円筒中心軸とする回転アニメーションをするのはどのようにすればよいのでしょうか?
X軸方向(axis.rotZ(Math.PI / 2.0 )を使って)、或いはZ軸方向に倒し、RotationInterpolatorを使っても、X軸、或いはZ軸を中心軸とした回転アニメーションにはならず、逆にZ軸、或いはX軸廻りに回転する結果となってしまいます。これは、回転軸に関してはY軸が基本で、円筒を倒すと同時にY軸も円筒と共に90度倒れることによると思われます。
横置き円筒軸廻りの回転アニメーションはどのようにすればできるのでしょうか?

A 回答 (5件)

以下のサンプルでご確認ください。



デフォルトで、異常回転という状態になっています。
そして、AnimationSampleコンストラクタ内の2行のコメントアウトされて
いる部分を入れ替えていただければ、所望の回転になると思います。

なお、プログラムの構造は、大きく変わっています。
解りやすさを第一義に考えて、シーングラフの骨格を形成する
TransformGroup, BranchGroupはクラスレベル変数にしてあります。
そして、1つのメソッド内でシーングラフの骨格を形成した後、
ライティングや,変換,Interpolator,Shape を追加していく
という構成にしました。


実用上は、TransformGroup, BranchGroupは、Nodeを継承した1つの
クラスとしてコンポジション実装したいところなのですが、
とりあえず、簡易的に書きました。


あと、Quad4d等の基本的なクラスに関してですが、基本的にそれらを
直接扱う必要性はJava3Dでは殆どないと思います。
Matrix4d等も含めてそれらを使いこなせるならば、わざわざ制約の多い
Java3D上でプログラミングを行うよりも、OpenGL等で直接3Dプログラミングを
行うのが得策だと思うからです。

Java3Dの解説書で、配列等の解説を行っているのは、その筆者のJava3Dの
理解力がいまひとつなので数学の話でお茶を濁しているように個人的には
感じています。
Java3Dの頂点インデックスのつけ方,管理の仕方など、私の知る限り
これらを解説した書籍すら見たことがありません。
ですから、これらの本を読んでもコーヒーカップすら作れません。

とはいいつつも、Java3Dがシーングラフの概念をサポートしている点は、
1つの大きなアドバンテージです。



import javax.swing.*;
import java.awt.BorderLayout;
import java.awt.GraphicsConfiguration;
import java.util.ArrayList;
import java.util.List;

import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import com.sun.j3d.utils.geometry.Cylinder;
import com.sun.j3d.utils.geometry.Box;
import javax.vecmath.*;

/* BranchGroup 3 (回転アニメーション)*/
public class AnimationSample extends JFrame {

public RotationInterpolator createRotationInterpolator( TransformGroup trans ) {
Alpha alpha = new Alpha(
-1,
Alpha.INCREASING_ENABLE,
0,
0,
1000,
400,
0,
0,
0,
0
);

Transform3D axis = new Transform3D();

RotationInterpolator rotat =
new RotationInterpolator(alpha, trans, axis, 0.0f, (float)Math.PI * 2);
BoundingSphere bounds = new BoundingSphere(new Point3d(), 100.0);
rotat.setSchedulingBounds(bounds);
trans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
trans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
return rotat;
}

public Transform3D createTransform3D() {
/* 変位移動はなし */
Transform3D t3d1 = new Transform3D();
t3d1.setTranslation(new Vector3f(0.0f, 0.0f, 0.0f));

/* Z軸廻りに90度回転 */
Transform3D t3d2 = new Transform3D();
t3d2.rotZ(Math.PI / 2);

/* Transform3Dでの合成 */
t3d1.mul(t3d2);
return t3d1;
}

public Light createLight(){
DirectionalLight light = new DirectionalLight( true,
new Color3f(0.0f, 0.0f, 1.0f),
new Vector3f(0.0f, 0.0f, -1.0f));

light.setInfluencingBounds(new BoundingSphere(new Point3d(), 100.0));

return light;
}

public List<Node> createShape3Ds() {
List<Node> shapes = new ArrayList<Node>();

Appearance ap = new Appearance();
Material ma = new Material();
ma.setDiffuseColor(0.0f, 0.0f, 1.0f);
ap.setMaterial(ma);

shapes.add( new Cylinder( 0.3f, 0.6f, Cylinder.GENERATE_NORMALS, 50, 1, ap) );
shapes.add( new Box( 0.4f, 0.1f, 0.1f, ap) );
return shapes;
}

public void createSceneGraph() {

// シーングラフ単位の生成と接続
trans1 = new TransformGroup();
branch1 = new BranchGroup();
trans1.addChild( branch1 );

trans2 = new TransformGroup();
branch2 = new BranchGroup();
trans2.addChild( branch2 );

trans3 = new TransformGroup();
branch3 = new BranchGroup();
trans3.addChild( branch3 );

// シーングラフ単位を縦続接続
branch1.addChild( trans2 );
branch2.addChild( trans3 );
}

public AnimationSample() {
getContentPane().setLayout(new BorderLayout());

GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();

Canvas3D canvas = new Canvas3D(config);
getContentPane().add(canvas, BorderLayout.CENTER);
SimpleUniverse universe = new SimpleUniverse(canvas);
universe.getViewingPlatform().setNominalViewingTransform();
BranchGroup rootBranch = new BranchGroup();

createSceneGraph();
rootBranch.addChild( createLight() );
rootBranch.addChild( trans1 );
trans1.addChild( createRotationInterpolator(trans1) );
trans2.setTransform( createTransform3D() );
// trans1.setTransform( createTransform3D() );
// trans2.addChild( createRotationInterpolator(trans2) );

for( Node shape : createShape3Ds() ) {
branch3.addChild( shape );
System.out.println(shape);
}

universe.addBranchGraph( rootBranch );
}

public static void main(String[] args) {
AnimationSample sample = new AnimationSample();

sample.setBounds( 10, 10, 240, 240);
sample.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
sample.setVisible(true);
}

private TransformGroup trans1;
private TransformGroup trans2;
private TransformGroup trans3;
private BranchGroup branch1;
private BranchGroup branch2;
private BranchGroup branch3;

}
    • good
    • 0
この回答へのお礼

HarukaV49さん、今回は本当にお世話になりました。
懇切ご丁寧な説明と最後にはソースにも説明を付けていただき誠にありがとうございました。
お陰様にて、今回の課題は解決完成することができました。

教えていただいたソースと実行結果をSample1とSample2とし、当初からの悪例に加え、下記のサイトに示しました。

http://www.geocities.jp/java3dtest/RotationTest1 …

ご確認いただければ幸いです。

前述しましたように、私はJavaに疎い者ですから、教えていただいたソースの内容につきましては、少し時間をかけて勉強させていただきたいと思います。

また、今回の課題は、私が若い頃携わってきた回転機械のアニメーション化をしたいと思い、Java3Dをはじめたところ、自分では解決できなかったためお願いした次第です。今後もまだまだ課題が山積みですので、また機会がありましたらご指導ください。

本当にありがとうございました。   taihey

お礼日時:2008/01/30 22:04

前回の回答で、私の思い込みから色々惑わすようなことを書いてしまいました。



シーングラフの構造は、

         SimpleUniverse
   |        |        |
 BranchGroup1  BranchGroup2   BranchGroup3
   |        |        |
TransformGroup1 TransformGroup2 TransformGroup3

となっていますね。


シーングラフの構成の仕方として

   |
TransformGroup
   |
 BranchGroup


を、1つのシーングラフの単位と考えましょう。
どういうことかというと、
 腕(BranchGroup)を関節(TransformGroup)によって接続していく
という感じです。
逆になっていると、動かない腕の先に関節が接続されている様になっていて
不自然な感じになることがお解かりいただけるでしょうか。

また、TransformGroupで終わっているシーングラフは、実用上も、
TransformGroupには動的にノードをaddChild(),detouch()できないという
制約があるため応用範囲を狭めてしまいます。


SimpleUniverseにSimpleUniverse#addBranchGraph(ルート(ダミー)のBranchGroup)
とした後に順次

   |
TransformGroup
   |
 BranchGroup



親のBranchGroup#.addChild(子のTransformGroup)
という風に、順次、従属で接続して行って下さい。

この回答への補足

HarukaV49さん、ANo.3並びにANo.4とご親切な説明をしていただきありがとうございます。

説明していただいたことは、初心者の私にも「なるほど」とわかるのですが、いざプログラミングに反映させようとすると知識も不足しており難しくなってきます。ご説明と併せ、にわか勉強でシーングラフ、BranchGroup、TransformGroup関連の資料を取り寄せて見ているのですが、Eclipse上で書き換えてはトライしっているのですが上手くいきません。

昨日、わからないままに書いてみたJavaソースは見ていただいたとおり、"Animation1.java"では、BranchGroup1、BranchGroup2、BranchGroup3をそれぞれ物体配置、座標系での静的回転、回転アニメーションとし、最終段階で下記のようにしております。この部分を教えていただいたように書き直せば良いのだとは思うのですが、その具体的方法が未熟なために残念ながらできていません。

BranchGroup scene1 = createSceneGraph1();
BranchGroup scene2 = createSceneGraph2();
BranchGroup scene3 = createSceneGraph3();
SimpleUniverse universe = new SimpleUniverse(canvas);

universe.getViewingPlatform().setNominalViewingTransform();
universe.addBranchGraph(scene1);
universe.addBranchGraph(scene2);
universe.addBranchGraph(scene3);

誠に申し訳ないのですが、該当部分、そして全体的にも書き直したほうがよい箇所をソースで示していただきたいと存じますが、お願いできるでしょうか。

私がJava3Dでアニメにしたい課題の初歩でつまずいており、教えていただいたことをベースに、自分でプログラミングできるようにしていきたいと存じますが、今回はよろしくお願いいたします。

補足日時:2008/01/30 11:25
    • good
    • 0

どこを勘違いされておられるかを確認しました。




現行のシーングラフの構造は、

         SimpleUniverse
   |        |        |
TransformGroup1 TransformGroup2 TransformGroup3
   |        |        |
 BranchGroup1  BranchGroup2   BranchGroup3

という接続になってしまっています。

 BranchGroup3#addChild( TransformGroup2 )

という風に従属で接続していく必要があります。
従属で接続するからこそ、そのノードから下のBranchが
ノードのTransformに依存するということです。


ソースコードまでは提供していませんが、
そこまでは必要ないですよね?
    • good
    • 0

シーングラフで考えた場合に、どのノードに対してRotationInterpolatorを


適用すべきかを考えましょう。

物体の回転後にも物体の回転とは無関係に、スクリーンで見ている座標系に対して
同じ角度で回転させ続けるためには、

  |
BranchGroup
  |
TransformGroup ← ここにRotationInterpolator適用
  |
BranchGroup
  |
TransformGroup ← ここの座標系で静的回転
  |
BranchGroup
  |
 Shape3D

となり、逆に上のTransformGroupを入れ替えると、RotationInterpolatorは
物体の回転にあわせて回転軸が変わっていくことになります。

コツとしては、上記のようにノードを別々にしておくことです。
間違わなければ1つのノードに対して、アフィン変換を順次乗算していくほうが
ソースコードは短く、解りやすいように見えますが、変更を加えようとしたときに
予想外の動きになってしまうことが良くあります。


また、クォータニオンが有効なのは、途中の姿勢が自由(任意)な場合の
2つの回転体の途中の姿勢を補間する場合です。
今回のように途中の姿勢にも意味がある場合の回転に対しては、
全く無関係であると思われます。
回転軸の1変数に対して変動させることで、途中の姿勢は定まっているわけですから。


クォータニオンの実用性に関しては、誤解しておられる方も多いので、
2つほど例を挙げてみます。

1.これが最も実用的なのですが、多軸のロボットアームがある角度から別の
ある角度に移動する場合に、複数の自由度があることが簡単に想像できると
思います。この時、クォータニオンを使って補間して移動させると、誰が
プログラミングを実装使用しても同じ動作をすることが保証できます。
2.空中に投げた物体をストロボ撮影した連続写真があったとします。
この場合、その写真の連続性が不十分な中で、その物体の写真と写真の間での
物体の状態を知りたかったとします。このとき、物体の位置に関しては、
2次関数補間を行えばそれらしい位置が求まることは想像に難くないでしょう。
では、物体の回転(状態)角度は? その答えがクォータニオンによる補間です。

この回答への補足

HarukaV49さん、ご丁寧な回答ありがとうございました。
早速、教えていただいた方法でトライしてみました。

すなわち、BranchGroupを3つ作り、最初のGroupにはRotastionInterpolatorを、第2のグループには静的回転を、第3のGroupには物体円筒形と回転が分かるようにするために間に直方体を挿入しました。

しかし、結果は物体は表示されるのですが回転すらしませんでした。(テスト1)

そこで、TransformGroupを逆にしてみましたが、これも同じく物体は表示されましたが、回転はしませんでした。(テスト2)

私は初心者でして、Javaプログラミングの記述がよくわかっていないこともあって、プログラミングのどこに誤りがあるのか見当がつきません。

そこで、竪置き円筒を転倒させないで回転するケース(これは正常に表示されます。)と教えていただく前に横置きにして円筒中心軸廻りの回転を狙ってトライしたケース(これは思惑を外れた異常回転をしました)を併せて4ケースにつきまとめてjavaソース(プログラミング)とその結果をjarファイルにしてまとめてRotationTest1.htmlとして下記にアップいたしました。

http://www.geocities.jp/java3dtest/RotationTest1 …

誠に恐れ入りますが、テスト1とテスト2のjavaソース(プログラミング)をご覧いただき、誤りをご指摘いただけないでしょうか?

なにとぞ、よろしくお願いいたします。

補足日時:2008/01/29 21:18
    • good
    • 0

私もルービックキューブを作ろうと思って


この問題にぶち当たったことがありました。
クォータニオンで検索して、頑張ってください。

この回答への補足

SAKENOSAKAさん、早速アドバイスをありがとうございました。
アドバイスしていただいたクォータニオンに関する資料「かんたんJava3D/付録:javax.vecmathパッケージ詳説(www.objectclub.jp/download/files/vecmath/vecmath.pdf)」を参照し、確かにこれを使って問題としていることが理解できそうだということはわかりました。
ただ、私の現在のレベルでは理解して使えるようにするのは難しく相当の時間をかけなければならないこともわかりました。
また、javax.vecmathにあるクラスQuat4f、或いはQuat4dの使い方もわかりやすく解説した資料、更に使い方を示すサンプルなども現時点では見つかっておりません。
何かJava初心者にも理解しやすい参考となるサイトなどいくつかご紹介いただければと存じます。

補足日時:2008/01/28 20:04
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!