プロが教える店舗&オフィスのセキュリティ対策術

Java3Dで円錐台を描きたいのですが、その方法が思いつかず、ご存知のかたがいらっしゃいましたら教えてください。

Java3Dには、円錐や円筒などの物体は基本図形を描くcom.sun.j3d.utils.geometryクラスにConeクラスやCylinderクラスがあり簡単にできるのですが、私の場合円錐台を二段重ねで描きたい課題があります。加えてこの二段重ねの円錐台の中には円筒軸を配置したく、外側に配置する二段の円錐台を透明にしたいという希望もあります。

二段重ねの円錐台(外側配置:透明)と円筒軸(内側配置)を横置きにして
1)RotationInterpolatorを使い回転アニメを作成
2)Behaviorクラスを使いマウス操作で物体を動かす
の2つのプログラミングを行いました。

共にConeクラスで配置位置を変えて、右に配置するConeの先端を隠す方法にて作成をしてみたのですが、1)については何とかそれらしいものが描けたのですが、2)については先端を隠すことができません。

また、2)については、物体の上をマウスでグリグリ動かすとマウスの動きに合わせて位置を変えることができるのですが、位置によっては円筒軸の色が消えて見難くなります。

更に1)と2)の両ケースともに、左右のconeの重ねた部分については、左側に配置したConeの先端部分の残像が表れてしまいます。

これらの問題を解決するには、どのようにしたらよろしいでしょうか?

A 回答 (6件)

前回のコードを TrancatedCone.java で保存するのはあっています。


そして、ご想像の通り前回のコードにはメインメソッドはありませんし、
3Dキャンバスやフレーム等も生成していませんので、そのまま
実行できるものでは有りません。
要するに、組込のJFrame.class, Cone.class 等と全く同じだということです。

ご自身の作製されたコードの中の 例えば
 Cone cone = new Cone( ...
という部分を
 TrancatedCone cone = new TrancatedCone( ...
に変更していただければ、そのまま動くと思います。
(TrancatedCone.javaが適当な場所(パッケージ)に保存されていて
 それが参照できているかどうかというだけの問題です)


下に、前回の TrancatedCone.java クラスを実行するためのサンプルコードを示しました。
Eclipse で実行する場合には、今回のクラスを TrancatedConeSample.java を
TrancatedCone.java と同じプロジェクトの同じパッケージ内に保存するのが
最も簡単な方法だと思います。
そして、TrancatedConeSample.javaのソースコード上で、右クリックすれば
[実行]-[アプリケーションの実行]が選択できると思います。



import javax.media.j3d.Appearance;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.Bounds;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.DirectionalLight;
import javax.media.j3d.Light;
import javax.media.j3d.Material;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.swing.JFrame;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector3f;

import com.sun.j3d.utils.behaviors.mouse.MouseRotate;
import com.sun.j3d.utils.behaviors.mouse.MouseTranslate;
import com.sun.j3d.utils.behaviors.mouse.MouseZoom;
import com.sun.j3d.utils.geometry.Cone;
import com.sun.j3d.utils.geometry.Sphere;
import com.sun.j3d.utils.universe.SimpleUniverse;

/**
* 円錐,円錐台,球 を生成するサンプルコード
*/
public class TrancatedConeSample {

public TrancatedConeSample() {

/* 円錐を生成 */
Appearance coneAppearance = new Appearance();
coneAppearance.setMaterial( new Material( new Color3f(0.2f,0.2f,0.2f), new Color3f(0,0,0), new Color3f(1,0,0), new Color3f(1,1,1), 64 ) );
Cone cone = new Cone( 0.3f, 0.4f, coneAppearance );
TransformGroup coneGroup = new TransformGroup();
Transform3D coneT3D = new Transform3D();
coneT3D.setTranslation( new Vector3d(-0.6, 0.0, 0.0) );
coneGroup.setTransform( coneT3D );
coneGroup.addChild( cone );

/* 円錐台を生成 */
Appearance truncatedConeAppearance = new Appearance();
truncatedConeAppearance.setMaterial( new Material( new Color3f(0.2f,0.2f,0.2f), new Color3f(0,0,0), new Color3f(0,1,0), new Color3f(1,1,1), 64 ) );
TruncatedCone truncatedCone = new TruncatedCone( 0.1, 0.3, 0.4, truncatedConeAppearance );
TransformGroup truncatedConeGroup = new TransformGroup();
Transform3D truncatedConeT3D = new Transform3D();
truncatedConeT3D.setTranslation( new Vector3d(0.0, 0.0, 0.0) );
truncatedConeGroup.setTransform( truncatedConeT3D );
truncatedConeGroup.addChild( truncatedCone );

/* 球を生成 */
Appearance sphereAppearance = new Appearance();
sphereAppearance.setMaterial( new Material( new Color3f(0.2f,0.2f,0.2f), new Color3f(0,0,0), new Color3f(0,0,1), new Color3f(1,1,1), 64 ) );
Sphere spere = new Sphere( 0.3f, sphereAppearance );
TransformGroup spereGroup = new TransformGroup();
Transform3D spereT3D = new Transform3D();
spereT3D.setTranslation( new Vector3d(+0.6, 0.0, 0.0) );
spereGroup.setTransform( spereT3D );
spereGroup.addChild( spere );

TransformGroup group = new TransformGroup();
group.setCapability( TransformGroup.ALLOW_TRANSFORM_READ );
group.setCapability( TransformGroup.ALLOW_TRANSFORM_WRITE );
group.addChild( coneGroup );
group.addChild( truncatedConeGroup );
group.addChild( spereGroup );

/* マウスによるキャンバス操作設定 */
MouseTranslate mouseTranslate = new MouseTranslate( group );
MouseRotate mouseRotate = new MouseRotate( group );
MouseZoom mouseZoom = new MouseZoom( group );
group.addChild( mouseTranslate );
group.addChild( mouseRotate );
group.addChild( mouseZoom );
Bounds bounds = new BoundingSphere( new Point3d(0,0,0), 100 );
mouseTranslate.setSchedulingBounds( bounds );
mouseRotate.setSchedulingBounds( bounds );
mouseZoom.setSchedulingBounds( bounds );

Light light = new DirectionalLight( new Color3f(1,1,1), new Vector3f(-1,-1,-1) );
light.setInfluencingBounds( bounds );

BranchGroup branch = new BranchGroup();
branch.addChild( light );
branch.addChild( group );

/* 最も基本的な3Dキャンバス生成 */
Canvas3D canvas = new Canvas3D( SimpleUniverse.getPreferredConfiguration() );
SimpleUniverse universe = new SimpleUniverse( canvas );
universe.getViewingPlatform().setNominalViewingTransform();
universe.addBranchGraph( branch );

JFrame frame = new JFrame();
frame.add( canvas );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setSize( 300, 300 );
frame.setVisible( true );

}

public static void main(String[] args) {
new TrancatedConeSample();
}

}

この回答への補足

HarukaV49さん、ご丁寧な回答をいただきありがとうございました。

とりあえず、Eclipseにて、示していただいた「TrancatedConeSample」ソースを前回のコード「TruncatedCone」と同じパッケージに保存し、[実行]-[アプリケーションの実行]をしたところ、左から「赤色コーン」、「緑色円錐台」、「青色球」を表示させることができました。

このような方法は私にとって初めてのことでしたので、また少し時間をかけていろいろトライしたいと思います。

とりあえずお礼まで。

お礼ポイントの方は、おって送らせていただきたいと思います。

いろいろとテーマを抱えており、また今後も教えていただく機会があろうかと思います。今後とも、よろしくお願いいたします。

今回は、誠にありがとうございました。

補足日時:2008/07/11 21:54
    • good
    • 0
この回答へのお礼

何回にもわたりご丁寧な回答をいただきありがとうございました。

同じ件名の質問に対して、何回かの回答の場合、各々にお礼のポイントを贈らせていただけるのかどうか不明ですが、とりあえずANo3に贈らせていただきたいと思います。

お礼日時:2008/07/14 20:15

>両方とも、私の作成したメインコードの最後部分(下記)のコードを直せば


>解決するのではないかと考えていますが、その具体的修正方法がわからず、
>追加質問をさせていただきました。よろしくお願いいたします。

問題は、ここではありません。
シーングラフの概念の理解が曖昧なようです。

私とのコード上の本質的な違いは、Lightオブジェクトのシーングラフ上への
接続位置の違いです。

あなたのコードでは、MouseRotateとLightが同一のTransformGroupに接続されています。
ですから、MouseRotateによって回転させると、Lightの位置も移動します。
私の場合は、Lightは、MouseRotateが接続されたTransformGroupの親のBranchGroupに
接続されています。ですから、物体を回転させてもライトの位置は変わりません。
この違いです。

サンプルコードまでは示しませんが、シーングラフの概念をしっかり理解しましょう。

この回答への補足

HarukaV49さん、早速お返事いただきありがとうございました。

ご指摘の点を認識しました。

私なりに変更し、各物体の底も赤、緑、青が表示されるようになりました。下記にアップしなおしました。

変更したコードがHarukaV49さんの作成されたアプリケーション方式をアプレット方式に変更した点を除き、全く同一になっているかどうか不明ですが、結果はほぼ同じではないかと思います。

何度もご親切な回答、アドバイスをいただき、ありがとうございました。

補足日時:2008/07/14 16:17
    • good
    • 0
この回答へのお礼

追加質問についても解決方法を教えていただきありがとうございました。

お陰様で、別途作成中だったbehaviorクラスについても上手くいきました。

できることなら、この回答にたいしてもポイントを贈らせていただきたいと思うのですが。

お礼日時:2008/07/14 21:17

手直しするといっても、そもそも何を目的としているか


不明ですので難しいです。
最終目的によっては、クラス設計(構造)そのものが
最適で無い場合もあるでしょう。

何を実現したくて、何ができていないのか、その部分のコードのみを
抽出していただけませんか?
現実には、そうして最小コードでデバッグすることで、自己解決
できることも多いと思います。

この回答への補足

HarukaV49さん、失礼いたしました。

私は、javaの初心者ですが、私が取り組んできたある横置き回転機械をJava3Dの1)RotationInterpolatorによる回転アニメーションで動きのあると、2)MouseBehaviorにより物体をマウスで動かして構造を容易にわかるようにしたく、Java3Dで実現できるのではないかと考えて取り組んできました。

その横置き回転機械の形状が一部、今回の課題で話題にさせていただいた「円錐台」をしているものですから、当初の質問をさせていただきました。この質問については、プリミティブに用意された基本形状ではできないことを認識しましたが、お陰様にて他の教えていただいた方法などを駆使して何とか描かせることができる目途がたちました。

今回のテーマにつきましては、これで終わったと考えてもよろしかったのですが、HarukaV49さんが示していただいたコードでは赤、緑、青の3物体をマウスで動かすと、底もそれぞれ赤、緑、青で表示されます。

HarukaV49さんのコードはアプリケーションで作成されたものですが、私が見よう見まねで作成したアプレット方式では3物体をマウスで動かしても底はすべてグレーとなっています。

そして、別途私が作成中の横置き回転機械もMouseBehaviorクラスで作成していますが、これもマウスの動きによっては指定した色が消え、黒色に変わり見えなくなります。

両方とも、私の作成したメインコードの最後部分(下記)のコードを直せば解決するのではないかと考えていますが、その具体的修正方法がわからず、追加質問をさせていただきました。よろしくお願いいたします。

// DirectionalLightを作成する
private Light createLight(){
DirectionalLight light = new DirectionalLight( true,
new Color3f(1.0f, 1.0f, 1.0f),
new Vector3f(-1.0f, -1.0f, -1.0f));
light.setInfluencingBounds(new BoundingSphere(new Point3d(), 100.0));

return light;
}

// AmbientLight を作成する
private Light createAmbientLight(){
AmbientLight light = new AmbientLight();

// 照明が影響する範囲を指定する
BoundingSphere bounds = new BoundingSphere(new Point3d(), 10.0);
light.setInfluencingBounds(bounds);

return light;
}

補足日時:2008/07/14 11:00
    • good
    • 0

TruncatedCone.java は、こちらをお使いください。


フラグやx,y分割数等に関してプリミティブの仕様に完全に一致させたつもりです。
前回のコードは、破棄してください。



import javax.media.j3d.Appearance;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.Group;
import javax.media.j3d.PolygonAttributes;
import javax.media.j3d.QuadArray;
import javax.media.j3d.Shape3D;
import javax.media.j3d.TriangleFanArray;
import javax.vecmath.Point3d;

import com.sun.j3d.utils.geometry.GeometryInfo;
import com.sun.j3d.utils.geometry.NormalGenerator;
import com.sun.j3d.utils.geometry.Primitive;
import com.sun.j3d.utils.geometry.Stripifier;

/**
* プリミティブ図形と同等なコンストラクタによって、円錐台を生成します。
* 用法もプリミティブ図形と同じです。
*/
public class TruncatedCone extends Group {

/** 上面を表すpartID */
public static final int TOP = 0;
/** 側面を表すpartID */
public static final int BODY = 1;
/** 底面を表すpartID */
public static final int BOTTOM = 2;

/** 法線を生成するための定数 */
public static final int GENERATE_NORMALS = Primitive.GENERATE_NORMALS;
/** 裏面の法線を生成するための定数 */
public static final int GENERATE_NORMALS_INWARD = Primitive.GENERATE_NORMALS_INWARD;
/** テクスチャ座標を生成するための定数 */
public static final int GENERATE_TEXTURE_COORDS = Primitive.GENERATE_TEXTURE_COORDS;
/** ピッキングを許可するための定数 */
public static final int ENABLE_GEOMETRY_PICKING = Primitive.ENABLE_GEOMETRY_PICKING;
/** アピアランスの更新を許可するための定数 */
public static final int ENABLE_APPEARANCE_MODIFY = Primitive.ENABLE_APPEARANCE_MODIFY;

/**
* 上面半径 0.5 底面半径 1.0 高さ 2.0 の円錐台を生成します。
*/
public TruncatedCone() {
this( 0.5, 1.0, 2.0 );
}
/**
* 上面半径, 底面半径, 高さ を指定して円錐台を生成します。
* @param topRadius 上面半径
* @param bottomRadius 底面半径
* @param height 高さ
*/
public TruncatedCone( double topRadius, double bottomRadius, double height ) {
this( topRadius, bottomRadius, height, GENERATE_NORMALS );
}
/**
* 上面半径, 底面半径, 高さ 及び アピアランスを指定して円錐台を生成します。
* @param topRadius 上面半径
* @param bottomRadius 底面半径
* @param height 高さ
* @param appearance アピアランス
*/
public TruncatedCone( double topRadius, double bottomRadius, double height, Appearance appearance ) {
this( topRadius, bottomRadius, height, GENERATE_NORMALS, 15, 1, appearance );
}
/**
* 上面半径, 底面半径, 高さ 及び フラグを指定して円錐台を生成します。
* @param topRadius 上面半径
* @param bottomRadius 底面半径
* @param height 高さ
* @param primflags フラグ
*/
public TruncatedCone( double topRadius, double bottomRadius, double height, int primflags ) {
this( topRadius, bottomRadius, height, primflags, 15, 1, null );
}

/**
* 円錐台を生成します。
* 引数の意味は基本的に組込のプリミティブ図形と同じです。
* @param topRadius 上面半径
* @param bottomRadius 底面半径
* @param height 高さ
* @param primflags フラッグ
* @param xDivision x方向分割数
* @param xDivision y方向分割数
* @param appearance アピアランス
*/
public TruncatedCone( double topRadius, double bottomRadius, double height, int primflags, int xDivision, int yDivision, Appearance appearance ) {

boolean isNormals = (primflags & GENERATE_NORMALS) == GENERATE_NORMALS;
boolean isTexture = (primflags & GENERATE_TEXTURE_COORDS) == GENERATE_TEXTURE_COORDS;
int geometryFlags = GeometryArray.COORDINATES | (isNormals ? GeometryArray.NORMALS : 0) | (isTexture ? GeometryArray.TEXTURE_COORDINATE_4 : 0);

boolean isBackFace = (primflags & GENERATE_NORMALS_INWARD) == GENERATE_NORMALS_INWARD;
boolean isGeometryPicking = (primflags & ENABLE_GEOMETRY_PICKING) == ENABLE_GEOMETRY_PICKING;
boolean isAppearanceModify = (primflags & ENABLE_APPEARANCE_MODIFY) == ENABLE_APPEARANCE_MODIFY;

if( isBackFace ) {
if( appearance == null ) {
appearance = new Appearance();
}
appearance.setPolygonAttributes( new PolygonAttributes( PolygonAttributes.POLYGON_FILL, PolygonAttributes.CULL_FRONT, 0, isBackFace ) );
}
if( isAppearanceModify ) {
if( appearance == null ) {
appearance = new Appearance();
}
appearance.setCapability( Appearance.ALLOW_POLYGON_ATTRIBUTES_READ );
appearance.setCapability( Appearance.ALLOW_POLYGON_ATTRIBUTES_WRITE );
}

addChild( new Shape3D( getTop(topRadius, height, xDivision, geometryFlags, isGeometryPicking, isNormals), appearance ) );
addChild( new Shape3D( getBody(topRadius, bottomRadius, height, xDivision, yDivision, geometryFlags, isGeometryPicking, isNormals), appearance ) );
addChild( new Shape3D( getBottom(bottomRadius, height, xDivision, geometryFlags, isGeometryPicking, isNormals), appearance ) );

}

/* 上面作製部 */
private GeometryArray getTop( double topRadius, double height, int xDivision, int geometryFlags, boolean isGeometryPicking, boolean isNormals ) {
GeometryArray topSurface = new TriangleFanArray( xDivision+2, geometryFlags, new int[]{xDivision+2} );
if( isGeometryPicking ) {
topSurface.setCapability( GeometryArray.ALLOW_INTERSECT );
}
Point3d[] topGeometries = new Point3d[xDivision+2]; // 上面座標
topGeometries[0] = new Point3d(0.0,height/2,0.0);
for(int i=0; i<=xDivision; i++) {
double theta = -2*Math.PI*i/xDivision;
topGeometries[i+1] = new Point3d( topRadius*Math.cos(theta), height/2, topRadius*Math.sin(theta) );
}
topSurface.setCoordinates( 0, topGeometries );
if( isNormals ) {
GeometryInfo topInfo = new GeometryInfo(topSurface);
NormalGenerator normalGenerator = new NormalGenerator();
normalGenerator.generateNormals( topInfo ); // 法線ベクトルの自動生成
return topInfo.getIndexedGeometryArray();
} else {
return topSurface;
}
}

/* 側面作製部 */
private GeometryArray getBody( double topRadius, double bottomRadius, double height, int xDivision, int yDivision, int geometryFlags, boolean isGeometryPicking, boolean isNormals ) {
GeometryArray bodySurface = new QuadArray( 4*xDivision*yDivision, geometryFlags );
if( isGeometryPicking ) {
bodySurface.setCapability( GeometryArray.ALLOW_INTERSECT );
}
Point3d[] bodyGeometries = new Point3d[4*xDivision*yDivision]; // 上面座標
bodyGeometries[0] = new Point3d(0.0,-height/2,0.0);
for(int i=0; i<xDivision; i++) {
double theta0 = 2*Math.PI*(i+0)/xDivision;
double theta1 = 2*Math.PI*(i+1)/xDivision;
Point3d top0 = new Point3d( topRadius*Math.cos(theta0), height/2, topRadius*Math.sin(theta0));
Point3d top1 = new Point3d( topRadius*Math.cos(theta1), height/2, topRadius*Math.sin(theta1));
Point3d btm0 = new Point3d(bottomRadius*Math.cos(theta0),-height/2,bottomRadius*Math.sin(theta0));
Point3d btm1 = new Point3d(bottomRadius*Math.cos(theta1),-height/2,bottomRadius*Math.sin(theta1));
for(int j=0; j<yDivision; j++) {
Point3d t0 = new Point3d(top0);
Point3d b0 = new Point3d(top0);
Point3d t1 = new Point3d(top1);
Point3d b1 = new Point3d(top1);
t0.interpolate( btm0, (double)(j+0)/yDivision );
b0.interpolate( btm0, (double)(j+1)/yDivision );
t1.interpolate( btm1, (double)(j+0)/yDivision );
b1.interpolate( btm1, (double)(j+1)/yDivision );
bodyGeometries[(i*yDivision+j)*4+0] = t0;
bodyGeometries[(i*yDivision+j)*4+1] = t1;
bodyGeometries[(i*yDivision+j)*4+2] = b1;
bodyGeometries[(i*yDivision+j)*4+3] = b0;
}
}
bodySurface.setCoordinates( 0, bodyGeometries );
if( isNormals ) {
GeometryInfo bodyInfo = new GeometryInfo(bodySurface);
NormalGenerator normalGenerator = new NormalGenerator();
normalGenerator.generateNormals( bodyInfo ); // 法線ベクトルの自動生成
Stripifier stripifier =new Stripifier();
stripifier.stripify( bodyInfo );
return bodyInfo.getIndexedGeometryArray();
} else {
return bodySurface;
}
}

/* 底面作製部 */
private GeometryArray getBottom( double bottomRadius, double height, int xDivision, int geometryFlags, boolean isGeometryPicking, boolean isNormals ) {
GeometryArray bottomSurface = new TriangleFanArray( xDivision+2, geometryFlags, new int[]{xDivision+2} );
if( isGeometryPicking ) {
bottomSurface.setCapability( GeometryArray.ALLOW_INTERSECT );
}
Point3d[] bottomGeometries = new Point3d[xDivision+2]; // 底面座標
bottomGeometries[0] = new Point3d(0.0,-height/2,0.0);
for(int i=0; i<=xDivision; i++) {
double theta = 2*Math.PI*i/xDivision;
bottomGeometries[i+1] = new Point3d( bottomRadius*Math.cos(theta), -height/2, bottomRadius*Math.sin(theta) );
}
bottomSurface.setCoordinates( 0, bottomGeometries );
if( isNormals ) {
GeometryInfo bottomInfo = new GeometryInfo(bottomSurface);
NormalGenerator normalGenerator = new NormalGenerator();
normalGenerator.generateNormals( bottomInfo ); // 法線ベクトルの自動生成
return bottomInfo.getIndexedGeometryArray();
} else {
return bottomSurface;
}
}

/**
* 指定パートのアピアランスを取得します。
* @param partId TOP, BODY, BOTTOM のいずれか
* @return アピアランス
*/
public Appearance getAppearance(int partId) {
return getShape(partId).getAppearance();
}
/**
* アピアランスを設定します。
* @param ap アピアランス
*/
public void setAppearance(Appearance ap) {
for(int i=0; i<3; i++) {
setAppearance( i, ap );
}
}
/**
* 指定パートの図形を取得します。
* @param partId TOP, BODY, BOTTOM のいずれか
* @return 図形
*/
public Shape3D getShape(int partId) {
return (Shape3D)getChild( partId );
}
/**
* 特定パートにのみアピアランスを設定します。
* @param partId TOP, BODY, BOTTOM のいずれか
* @param ap アピアランス
*/
public void setAppearance(int partId, Appearance ap ) {
getShape( partId ).setAppearance( ap );
}

}

この回答への補足

HarukaV49さん、更に「フラグやx,y分割数等に関してプリミティブの仕様に完全に一致させた」回答を付けていただきありがとうございました。

何分にも初心者なもので「プリミティブの仕様」がどのようになっているかなども未知ですので、前に回答いただいたコードと見比べながら時間をかけて勉強したいと思います。折角回答いただいた価値がすぐにわからず申し訳ありません。

教えていただいたコードは application スタイルでしたので、結果の表示には jar ファイルを作成し、クリックしたら見れるようにしましたが、これに自分が以前作成していたコードに教えていただいた心臓部分を盛り込み書き applet スタイルにしたものを追加しました。

但し、こののコードもみようみまねで作成したものであり、結果の図形をマウスでグリグリ動かし底面を表示させたりすると表示色が教えていただいたようにきれいな三色になっていません。光線の当て方などメインコードの記述を改めなければならないのでしょうが、どこをどのように改めたらよいのかわかりません。

他にも私の作成したメインコードには無駄な記述もあるかと思います。併せて修正・アドバイスをしていただければありがたいです。(尚、サブコードの TruncatedCone.java は変更しておりません。)

誠に恐縮ですがお願いできるでしょうか。

補足日時:2008/07/13 07:44
    • good
    • 0
この回答へのお礼

改めてTruncatedConeを送っていただきありがとうございました。
前回のは破棄するようにとのことでしたが、両コードとプリミティブ仕様との関係を比較しながら勉強したく存じます。

両コードを送っていただき、ありがとうございました。

お礼日時:2008/07/14 20:24

Java3Dでは、GeometryArrayのサブクラスを組み合わせて図形を作ります。


円錐台だと、例えば下のような方法で実現できます。



import javax.media.j3d.Appearance;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.Group;
import javax.media.j3d.PolygonAttributes;
import javax.media.j3d.QuadArray;
import javax.media.j3d.Shape3D;
import javax.media.j3d.TriangleFanArray;
import javax.vecmath.Point3d;

import com.sun.j3d.utils.geometry.GeometryInfo;
import com.sun.j3d.utils.geometry.NormalGenerator;
import com.sun.j3d.utils.geometry.Primitive;
import com.sun.j3d.utils.geometry.Stripifier;

/**
* プリミティブ図形と同等なコンストラクタによって、円錐台を生成します。
* 用法もプリミティブ図形と同じです。
*/
public class TruncatedCone extends Group {

/**
* partId用定数
*/
public static final int TOP = 0;
public static final int BODY = 1;
public static final int BOTTOM = 2;

/**
* primflags用定数
*/
public static final int GENERATE_NORMALS = Primitive.GENERATE_NORMALS;
public static final int GENERATE_NORMALS_INWARD = Primitive.GENERATE_NORMALS_INWARD;
public static final int GENERATE_TEXTURE_COORDS = Primitive.GENERATE_TEXTURE_COORDS;
public static final int ENABLE_GEOMETRY_PICKING = Primitive.ENABLE_GEOMETRY_PICKING;
public static final int ENABLE_APPEARANCE_MODIFY = Primitive.ENABLE_APPEARANCE_MODIFY;

/**
* 上面半径 0.5 底面半径 1.0 高さ 2.0 の円錐台を生成します。
*/
public TruncatedCone() {
this( 0.5, 1.0, 2.0 );
}
/**
* 上面半径, 底面半径, 高さ を指定して円錐台を生成します。
* @param topRadius 上面半径
* @param bottomRadius 底面半径
* @param height 高さ
*/
public TruncatedCone( double topRadius, double bottomRadius, double height ) {
this( topRadius, bottomRadius, height, GENERATE_NORMALS );
}
/**
* 上面半径, 底面半径, 高さ 及び アピアランスを指定して円錐台を生成します。
* @param topRadius 上面半径
* @param bottomRadius 底面半径
* @param height 高さ
* @param appearance アピアランス
*/
public TruncatedCone( double topRadius, double bottomRadius, double height, Appearance appearance ) {
this( topRadius, bottomRadius, height, GENERATE_NORMALS, 36, appearance );
}
/**
* 上面半径, 底面半径, 高さ 及び フラッグを指定して円錐台を生成します。
* @param topRadius 上面半径
* @param bottomRadius 底面半径
* @param height 高さ
* @param primflags フラッグ
*/
public TruncatedCone( double topRadius, double bottomRadius, double height, int primflags ) {
this( topRadius, bottomRadius, height, primflags, 36, null );
}

/**
* 円錐台を生成します。
* 引数の意味は基本的に組込のプリミティブ図形と同じです。
* @param topRadius 上面半径
* @param bottomRadius 底面半径
* @param height 高さ
* @param primflags フラッグ
* @param division 分割数
* @param appearance アピアランス
*/
public TruncatedCone( double topRadius, double bottomRadius, double height, int primflags, int division, Appearance appearance ) {

int geometryFlags = GeometryArray.COORDINATES |
((primflags & GENERATE_NORMALS) == GENERATE_NORMALS ? GeometryArray.NORMALS : 0) |
((primflags & GENERATE_TEXTURE_COORDS) == GENERATE_TEXTURE_COORDS ? GeometryArray.TEXTURE_COORDINATE_4 : 0);

boolean isBackFace = (primflags & GENERATE_NORMALS_INWARD) == GENERATE_NORMALS_INWARD;
boolean isGeometryPicking = (primflags & ENABLE_GEOMETRY_PICKING) == ENABLE_GEOMETRY_PICKING;
boolean isAppearanceModify = (primflags & ENABLE_APPEARANCE_MODIFY) == ENABLE_APPEARANCE_MODIFY;

if( isBackFace ) {
if( appearance == null ) {
appearance = new Appearance();
}
appearance.setPolygonAttributes( new PolygonAttributes( PolygonAttributes.POLYGON_FILL, PolygonAttributes.CULL_NONE, 0, isBackFace ) );
}
if( isAppearanceModify ) {
if( appearance == null ) {
appearance = new Appearance();
}
appearance.setCapability( Appearance.ALLOW_POLYGON_ATTRIBUTES_READ );
appearance.setCapability( Appearance.ALLOW_POLYGON_ATTRIBUTES_WRITE );
}


/* 上面作製部 */
GeometryArray topSurface = new TriangleFanArray( division+1, geometryFlags, new int[]{division+1} );
if( isGeometryPicking ) {
topSurface.setCapability( GeometryArray.ALLOW_INTERSECT );
}
Point3d[] topGeometries = new Point3d[division+1]; // 上面座標
topGeometries[0] = new Point3d(0.0,height/2,0.0);
for(int i=0; i<division; i++) {
topGeometries[i+1] = new Point3d(topRadius*Math.cos(-2*Math.PI*i/(division-1)),height/2,topRadius*Math.sin(-2*Math.PI*i/(division-1)));
}
topSurface.setCoordinates( 0, topGeometries );
GeometryInfo topInfo = new GeometryInfo(topSurface);
NormalGenerator normalGenerator = new NormalGenerator();
normalGenerator.generateNormals( topInfo ); // 法線ベクトルの自動生成
addChild( new Shape3D( topInfo.getIndexedGeometryArray(), appearance ) );

/* 側面作製部 */
GeometryArray bodySurface = new QuadArray( 4*(division-1), geometryFlags );
if( isGeometryPicking ) {
bodySurface.setCapability( GeometryArray.ALLOW_INTERSECT );
}
Point3d[] bodyGeometries = new Point3d[4*(division-1)]; // 上面座標
bodyGeometries[0] = new Point3d(0.0,-height/2,0.0);
for(int i=0; i<division-1; i++) {
double theta0 = 2*Math.PI*(i+0)/(division-1);
double theta1 = 2*Math.PI*(i+1)/(division-1);
bodyGeometries[(i*4+0)] = new Point3d( topRadius*Math.cos(theta0), height/2, topRadius*Math.sin(theta0));
bodyGeometries[(i*4+1)] = new Point3d( topRadius*Math.cos(theta1), height/2, topRadius*Math.sin(theta1));
bodyGeometries[(i*4+2)] = new Point3d(bottomRadius*Math.cos(theta1),-height/2,bottomRadius*Math.sin(theta1));
bodyGeometries[(i*4+3)] = new Point3d(bottomRadius*Math.cos(theta0),-height/2,bottomRadius*Math.sin(theta0));
}
bodySurface.setCoordinates( 0, bodyGeometries );
GeometryInfo sideInfo = new GeometryInfo(bodySurface);
normalGenerator.generateNormals( sideInfo ); // 法線ベクトルの自動生成
Stripifier stripifier =new Stripifier();
stripifier.stripify( sideInfo );
addChild( new Shape3D( sideInfo.getIndexedGeometryArray(), appearance ) );

/* 底面作製部 */
GeometryArray bottomSurface = new TriangleFanArray( division+1, geometryFlags, new int[]{division+1} );
if( isGeometryPicking ) {
bottomSurface.setCapability( GeometryArray.ALLOW_INTERSECT );
}
Point3d[] bottomGeometries = new Point3d[division+1]; // 上面座標
bottomGeometries[0] = new Point3d(0.0,-height/2,0.0);
for(int i=0; i<division; i++) {
bottomGeometries[i+1] = new Point3d(bottomRadius*Math.cos(2*Math.PI*i/(division-1)),-height/2,bottomRadius*Math.sin(2*Math.PI*i/(division-1)));
}
bottomSurface.setCoordinates( 0, bottomGeometries );
GeometryInfo bottomInfo = new GeometryInfo(bottomSurface);
normalGenerator.generateNormals( bottomInfo ); // 法線ベクトルの自動生成
addChild( new Shape3D( bottomInfo.getIndexedGeometryArray(), appearance ) );

}

/**
* 指定パートのアピアランスを取得します。
* @param partId TOP, BODY, BOTTOM のいずれか
* @return アピアランス
*/
public Appearance getAppearance(int partId) {
return getShape(partId).getAppearance();
}
/**
* アピアランスを設定します。
* @param ap アピアランス
*/
public void setAppearance(Appearance ap) {
for(int i=0; i<3; i++) {
setAppearance( i, ap );
}
}
/**
* 指定パートの図形を取得します。
* @param partId TOP, BODY, BOTTOM のいずれか
* @return 図形
*/
public Shape3D getShape(int partId) {
return (Shape3D)getChild( partId );
}
/**
* 特定パートにのみアピアランスを設定します。
* @param partId TOP, BODY, BOTTOM のいずれか
* @param ap アピアランス
*/
public void setAppearance(int partId, Appearance ap ) {
getShape( partId ).setAppearance( ap );
}

}

この回答への補足

円錐台を作成するプログラムを付けていただいての回答ありがとうございました。

ところが、残念ながら、何分にも私はJava初心者でこのプログラムから円錐台を表示させることができません。

私が試みた方法の1は、
1) プログラムをそのままコピーして、メモ帳に貼り、TruncatedCone.java の名前でjavaファイルを保存する。
2) 次に、そのファイルを コマンドプロンプトにて、javacコマンドでコンパイルする。(その結果、TruncatedCone.class はできました。)
3) 次に、コマンドプロンプトにて、javaコマンドで TruncatedConeを実行する。

その結果、下記のメッセージが表示されて円錐台は表示されませんでした。

Exception in thread “main” java.lang.NoSuchMethodError: main

第2の方法は、eclipseにて試みたのですが、クラス名をTruncatedConeとしてプログラムをコピー作成ひた時点ではエラーはなかったのですが、実行の段階で、実行メニューには「サーバーで実行」と「Java Been」だけがあり、通常では実行メニューに表示される「アプリケーション」と「アプレット」の表示がなく、eclipseにても円錐台を表示させることができていません。

円錐台を表示させるには、教えていただいたプログラムの他、例えばメインとなるプログラムとかが当然(?)必要なのかもしれませんが、どのように組み合わせて、コマンドプロンプトなり、eclipseなりでコンパイル・実行ということになるのでしょうが、私にはどのようなプログラムを組み合わせれば(?)よいのかなどもわからない次第にて、折角教えていただいたプログラムを活用できない状態です。

更に、ご指導いただけるでしょうか?よろしくお願いします。

補足日時:2008/07/11 09:39
    • good
    • 0

Java3Dは以前簡単に試したことがあるだけなので、ご参考までに。



com.sun.j3d.utils.geometry に用意されているプリミティブはごく基本的な形状のものばかりです。
それ以上複雑な形状の場合にこれらのプリミティブを組み合わせて実現するのはさすがに無理があります。

>左右のconeの重ねた部分については、左側に配置したConeの先端部分の残像が表れてしまいます。

これらもプリミティブを組み合わせて作ろうとしていることからきた限界だといえます。
重なった部分は合わさってひとつになったわけではないので、半透明にすると重なった部分が見えてしまいます。
また、真っ黒な Cylinder を重ねることで擬似的に円錐のてっぺんをを隠していらっしゃるようですが、
これは背景の色が変わったり他の物体の手前に位置したりすると破綻する、その場しのぎの方法でしかありません。

というわけで、結局プリミティブを組み合わせて何とかしようというのは限界があるので、自力でその図形を作る必要があるかと思います。
おそらく一番簡単な方法は3Dモデラでその形状を作って読み込むことだと思います。
Java3Dは Lightwave 3D シーンファイル( com.sun.j3d.loaders.lw3d ) と
Wavefront .obj ファイル ( com.sun.j3d.loaders.objectfile ) の読み込みが可能なようです。

もしくは、コード上でそのような形状を自力で生成するか、ということになるかと思います。
その場合は以下のようなコードを参考にするとよいかと思います。

http://www.google.com/codesearch?hl=ja&q=com.sun …

この回答への補足

hirusagariさん、回答いただきありがとうございます。

私が考えている形状を、プリミティブにある基本形状を利用して作成することはできないこと。また、プリミティブに存在しないことを改めて認識いたしました。

別の方法にて、自分で作成せざるを得ないのかと思いますが、ご回答の後半に書かれている内容は具体的にどのように利用すればよろしいのでしょうか?

http://www.google.com/codesearch?hl=ja&q=com.sun …

をクリックして先ず表示されるのは、「ColorCube.java」のプログラムですが、このプログラムソースをEclipseにコピーし描かせようとしても、下記のラインでエラーとなり、実行することができず、どのような物体が描かれるのか不明で、利用の仕方がわかりません。

package com.sun.j3d.utils.geometry;

私はJavaの初新者なもので、他の方の作られたプログラム・サンプルを見て利用させていただく進め方をしてきたものですから、教えていただいたコードをどのように利用していけばよいか、一例を示していただければありがたいのですが。

よろしくお願いします。

補足日時:2008/07/09 10:56
    • good
    • 0
この回答へのお礼

hirusagariさんからいただいたANo1がきっかけとなり、その後、HarukaV49さんから具体的にコードを示していただき、問題が解決しました。

ありがとうございました。

お礼日時:2008/07/14 21:22

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