Hi, all friends:
I encountered an odd case. see attached file.
I model a 3D shape made of a top cone, a middle cylinder and a bottom cone.
I wanted to use only one geometry.
So I generated the coordinates, vertices colors, and normals for three
elements separately first. Then I use one set arrays (coordinates, colors,
and normals) to hold all three elements' corresponding arrays together. At
the end I generated one GeometryArray object using that big set of arrays.
The odd thing is that in the 3D shape the last triangle is missing in each
of the cone geometry. If I use the separated array to generate the shape,
nothing is missing. Could someone please take a look and help me to figure
out what the problem is?
Thanks!
import java.awt.*;
import java.awt.event.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.geometry.*;
public class SharedTreeGeo implements GeometryUpdater
{
protected static float totalHt;
// = 8.f*0.305f; //meter, assum 1' tall for this group
protected static float trunkRadius;
// = .25f*0.305f/12.f; // .5" dia
protected static float trunkHt;
// = 0.55f*totalHt; // assume trunkHeight = 0.55*totalHeight
protected static float crownRadius;
// = 60*trunkRadius; // not statistic data
protected static float topConeRatio; //ratio of top cone over crown height
protected static float botConeRatio;
public GeometryArray geoms;
protected static int division = 6;
protected static double alpha = 2*Math.PI/division;
private static int totalVertex = 3*division;
Point3f geomsCoords[] = new Point3f[2*(3+6)*division+2];
protected Color3f geomsColors[] = new Color3f[2*(3+6)*division+2];
Vector3f[] geomsNormals = new Vector3f[18*division+2];
Vector3f[] topCNormals = new Vector3f[3*division];
Vector3f[] botCNormals = new Vector3f[3*division];
Vector3f[] trunkNormals = new Vector3f[2*3*division];
Vector3f[] crownCylNormals = new Vector3f[2*3*division];
public SharedTreeGeo() { }
public void buildTree()
{
GeometryArray topTriStrip, botTriStrip, crownQuad, trunkQuad;
Point3f topCoords[] = new Point3f[3*division];
Point3f botCoords[] = new Point3f[3*division];
// for top and bottom cones using TriangleStripArray
Point3f midCoords[] = new Point3f[2*3*division]; // for crown
quadArray
Point3f trunkCoords[] = new Point3f[2*3*division]; // for trunk
quadArray
Color3f coneColors[] = new Color3f[3*division];
Color3f cylindColors[] = new Color3f[2*3*division];
Color3f trunkColors[] = new Color3f[2*3*division];
float crownHt = totalHt-trunkHt;
float cylindCrHt = (1-topConeRatio-botConeRatio)*crownHt;
float botConeHt = botConeRatio*crownHt;
float topConeHt = topConeRatio*crownHt;
float cylTopHt = trunkHt+botConeHt+cylindCrHt;
float cylLowHt = trunkHt+botConeHt;
float x, z, xNext, zNext, xTr, zTr, xTrNext, zTrNext;
double a, aNext;
int n;
aNext=0;
for (a=0, n=0; n<division; a=alpha* ++n)
{ // the points generated for cones are in counter clockwise
x = (float) (crownRadius*Math.cos(a));
z = -(float) (crownRadius*Math.sin(a));
aNext = a+alpha;
if (n == 5)
{
xNext = crownRadius;
zNext = 0;
}
else
{
xNext = (float) (crownRadius*Math.cos(aNext));
zNext = -(float) (crownRadius*Math.sin(aNext));
}
// top cone five points
topCoords[3*n] = new Point3f(.0f, cylTopHt+topConeHt, .0f);
topCoords[3*n+1] = new Point3f(x, cylTopHt, z);
topCoords[3*n+2] = new Point3f(xNext, cylTopHt, zNext);
// if(n==5)
// {
System.out.println("topCX="+topCoords[3*n+1].x+"topCZ="+topCoords[3*n+1].z
//
+"topCX="+topCoords[3*n+2].x+"topCZ="+topCoords[3*n+2].z);
// }
// lower cone five points
botCoords[3*n] = new Point3f(.0f, trunkHt, .0f); //cone
tip, lower
botCoords[3*n+1] = new Point3f(xNext, trunkHt+botConeHt,
zNext);
botCoords[3*n+2] = new Point3f(x, trunkHt+botConeHt, z);
// Cylinder quadArray points, generated are in clockwise
midCoords[6*n] = new Point3f(x, cylTopHt, -z);
midCoords[6*n+1] = new Point3f(xNext, cylTopHt, -zNext);
midCoords[6*n+2] = new Point3f(x, cylLowHt, -z);
midCoords[6*n+3] = new Point3f(xNext, cylTopHt, -zNext);
midCoords[6*n+4] = new Point3f(x, cylLowHt, -z);
midCoords[6*n+5] = new Point3f(xNext, cylLowHt, -zNext);
// cal. trunk coord. points are in clockwise
xTr = (float) (trunkRadius*Math.cos(a));
zTr = (float) (trunkRadius*Math.sin(a));
// trunk quadArray
xTrNext = (float) (trunkRadius*Math.cos(aNext));
zTrNext = (float) (trunkRadius*Math.sin(aNext));
trunkCoords[6*n] = new Point3f(xTr, cylLowHt, zTr);
trunkCoords[6*n+1] = new Point3f(xTrNext, cylLowHt, zTrNext);
trunkCoords[6*n+2] = new Point3f(xTr, 0, zTr);
trunkCoords[6*n+3] = new Point3f(xTrNext, cylLowHt, zTrNext);
trunkCoords[6*n+4] = new Point3f(xTr, 0, zTr);
trunkCoords[6*n+5] = new Point3f(xTrNext, 0, zTrNext);
}
System.arraycopy(topCoords, 0, geomsCoords, 0, topCoords.length);
System.arraycopy(botCoords, 0, geomsCoords, topCoords.length,
botCoords.length);
System.arraycopy(midCoords, 0, geomsCoords, 2*topCoords.length,
midCoords.length);
System.arraycopy(trunkCoords, 0, geomsCoords,
2*topCoords.length+midCoords.length,
trunkCoords.length);
genConesNormal();
genCylindNormal();
System.arraycopy(topCNormals, 0, geomsNormals, 0, topCNormals.length);
System.arraycopy(botCNormals, 0, geomsNormals, topCNormals.length,
botCNormals.length);
System.arraycopy(crownCylNormals, 0, geomsNormals,
2*topCNormals.length, crownCylNormals.length);
System.arraycopy(crownCylNormals, 0, geomsNormals,
2*topCNormals.length+crownCylNormals.length, crownCylNormals.length);
Color3f Green = new Color3f( 0.0f, 1.0f, 0.0f );
Color3f brown = new Color3f( 0.78f, 0.2f, 0.1f );
for (int i=0; i<3*division; i++)
coneColors[i] = Green;
for (int i=0; i<6*division; i++){
cylindColors[i] = Green;
trunkColors[i] = brown;
}
System.arraycopy(coneColors, 0, geomsColors, 0, coneColors.length);
System.arraycopy(coneColors, 0, geomsColors, coneColors.length,
coneColors.length);
System.arraycopy(cylindColors, 0, geomsColors, 2*coneColors.length,
cylindColors.length);
System.arraycopy(trunkColors, 0, geomsColors,
2*coneColors.length+cylindColors.length,
trunkColors.length);
int[] stripVertexCounts = {18, 18, 36, 36};
geoms = new TriangleStripArray(108, // 3*division
TriangleStripArray.COORDINATES|
TriangleStripArray.COLOR_3|
TriangleStripArray.NORMALS|
TriangleStripArray.BY_REFERENCE,
stripVertexCounts);
geoms.setCoordRef3f(geomsCoords);
geoms.setColorRef3f(geomsColors);
geoms.setNormalRef3f(geomsNormals);
geoms.setCapability(GeometryArray.ALLOW_REF_DATA_WRITE);
}
public void genConesNormal(){
Vector3f one = new Vector3f( 0.0f, 0.0f, 0.0f );
Vector3f two = new Vector3f( 0.0f, 0.0f, 0.0f );
for (int i=0; i<division; i++)
{
one.set(geomsCoords[3*i+1].x-geomsCoords[3*i].x,
geomsCoords[3*i+1].y-geomsCoords[3*i].y,
geomsCoords[3*i+1].z-geomsCoords[3*i].z );
one.normalize();
two.set(geomsCoords[3*i+2].x-geomsCoords[3*i].x,
geomsCoords[3*i+2].y-geomsCoords[3*i].y,
geomsCoords[3*i+2].z-geomsCoords[3*i].z );
two.normalize();
topCNormals[3*i] = new Vector3f(0.0f, 0.0f, 0.0f );
topCNormals[3*i].cross( one, two );
topCNormals[3*i+1] = new Vector3f(0.0f, 0.0f, 0.0f );
topCNormals[3*i+1].cross( one, two );
topCNormals[3*i+2] = new Vector3f(0.0f, 0.0f, 0.0f );
topCNormals[3*i+2].cross( one, two );
botCNormals[3*i] = new Vector3f(0.0f, 0.0f, 0.0f );
botCNormals[3*i].cross(two, one);
botCNormals[3*i+1] = new Vector3f(0.0f, 0.0f, 0.0f );
botCNormals[3*i+1].cross( two, one );
botCNormals[3*i+2] = new Vector3f(0.0f, 0.0f, 0.0f );
botCNormals[3*i+2].cross( two, one );
}
}
public void genCylindNormal(){
Vector3f one = new Vector3f( 0.0f, 0.0f, 0.0f );
Vector3f two = new Vector3f( 0.0f, 0.0f, 0.0f );
for (int i=0; i<division; i++)
{
one.set(geomsCoords[6*i+1+2*3*division].x-geomsCoords[6*i+2*3*division].x,
geomsCoords[6*i+1+2*3*division].y-geomsCoords[6*i+2*3*division].y,
geomsCoords[6*i+1+2*3*division].z-geomsCoords[6*i+2*3*division].z );
one.normalize();
two.set(geomsCoords[6*i+2+2*3*division].x-geomsCoords[6*i+2*3*division].x,
geomsCoords[6*i+2+2*3*division].y-geomsCoords[6*i+2*3*division].y,
geomsCoords[6*i+2+2*3*division].z-geomsCoords[6*i+2*3*division].z );
two.normalize();
crownCylNormals[6*i] = new Vector3f(0.0f, 0.0f, 0.0f );
crownCylNormals[6*i].cross( one, two );
crownCylNormals[6*i+1] = new Vector3f(0.0f, 0.0f, 0.0f
);
crownCylNormals[6*i+1].cross( one, two );
crownCylNormals[6*i+2] = new Vector3f(0.0f, 0.0f, 0.0f
);
crownCylNormals[6*i+2].cross( one, two );
crownCylNormals[6*i+3] = new Vector3f(0.0f, 0.0f, 0.0f
);
crownCylNormals[6*i+3].cross( one, two );
crownCylNormals[6*i+4] = new Vector3f(0.0f, 0.0f, 0.0f
);
crownCylNormals[6*i+4].cross( one, two );
crownCylNormals[6*i+5] = new Vector3f(0.0f, 0.0f, 0.0f
);
crownCylNormals[6*i+5].cross( one, two );
}
}
public void update(float crRa, float topCRa, float botCRa)
{
// System.out.println("update");
trunkHt = (1-crRa)*totalHt;
topConeRatio = topCRa; //ratio of top cone over crown height
botConeRatio = botCRa;
updateData(geoms);
}
public void updateData(Geometry geometry)
{
// System.out.println("updateData");
float crownHt = totalHt-trunkHt;
float cylindCrHt = (1-topConeRatio-botConeRatio)*crownHt;
float botConeHt = botConeRatio*crownHt;
float topConeHt = topConeRatio*crownHt;
float cylTopHt = trunkHt+botConeHt+cylindCrHt;
float cylLowHt = trunkHt+botConeHt;
for (int n=0; n<division; n++)
{
geomsCoords[3*n].y = cylTopHt+topConeHt;
geomsCoords[3*n+1].y = cylTopHt;
geomsCoords[3*n+2].y = cylTopHt;
// lower cone five points
geomsCoords[3*n+3*division].y = trunkHt; //cone tip,
lower
geomsCoords[3*n+1+3*division].y = trunkHt+botConeHt;
geomsCoords[3*n+2+3*division].y = trunkHt+botConeHt;
// Cylinder quadArray points, generated are in clockwise
geomsCoords[6*n+2*3*division].y = cylTopHt;
geomsCoords[6*n+1+2*3*division].y = cylTopHt;
geomsCoords[6*n+2+2*3*division].y = cylLowHt;
geomsCoords[6*n+3+2*3*division].y = cylTopHt;
geomsCoords[6*n+4+2*3*division].y = cylLowHt;
geomsCoords[6*n+5+2*3*division].y = cylLowHt;
geomsCoords[6*n+12*division].y = cylLowHt;
geomsCoords[6*n+1+12*division].y = cylLowHt;
geomsCoords[6*n+3+12*division].y = cylLowHt;
}
if (geometry instanceof GeometryArray)
{
((GeometryArray) geometry).setCoordRef3f(geomsCoords);
genConesNormal();
System.arraycopy(topCNormals, 0, geomsNormals, 0,
topCNormals.length);
System.arraycopy(botCNormals, 0, geomsNormals,
topCNormals.length, botCNormals.length);
((GeometryArray)geometry).setNormalRef3f(geomsNormals);
}
}
public void setTotalHt(float thisHieight)
{
totalHt = thisHieight;
}
public void setTrunkRad(float thisRadius)
{
trunkRadius = thisRadius;
}
public void calTrunkHt()
{
trunkHt = 0.55f*totalHt; // assume trunkHeight =
0.55*totalHeight
}
public void setCrownRad(float thisCR)
{
crownRadius = thisCR;
}
public void setTopConeRatio(float thisRatio)
{
topConeRatio = thisRatio;
}
public void setBottomConeRatio(float thisRatio)
{
botConeRatio = thisRatio;
}
/* public void setTreeColor(Color3f thisColor)
{
for (int i=0; i<coneColors.length; i++)
coneColors[i] = thisColor;
for (int i=0; i<crownQuadColors.length; i++)
crownQuadColors[i] = thisColor;
}
*/
}
import java.applet.Applet;
import com.sun.j3d.utils.applet.MainFrame;
import java.awt.*;
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.*;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import com.sun.j3d.utils.behaviors.vp.*;
import com.sun.j3d.utils.behaviors.mouse.*;
public class Tree3ele2 extends Applet
{
protected final static Color3f White = new Color3f( 1.0f, 1.0f, 1.0f );
protected final static Color3f Green = new Color3f( 0.0f, 1.0f, 0.0f );
protected static final Color3f brown = new Color3f( 0.78f, 0.2f, 0.1f );
protected static final Color3f bkground = new Color3f(.761f, .973f, .969f);
private JButton button;
private static final int numbers = 2;
SharedTreeGeo[] trees = new SharedTreeGeo[numbers];
SharedTreeGeoM shTree;
private SimpleUniverse u;
private Canvas3D vFstCanvas3D;
private BoundingSphere bounds;
ViewingPlatform viewingPlatform;
public static TransformGroup vpTransformGroup, joyTransformGroup;
private Link link[] = new Link[numbers];
public void init() {
BranchGroup bg = new BranchGroup();
GraphicsConfiguration config =
SimpleUniverse.getPreferredConfiguration();
vFstCanvas3D = new Canvas3D( config )
{
Dimension canvasPrefSize = new Dimension( 600, 800 );
public Dimension getPreferredSize() { return
canvasPrefSize; }
};
u = new SimpleUniverse(vFstCanvas3D,2);
setLayout( new BorderLayout() );
add( vFstCanvas3D, BorderLayout.CENTER );
viewingPlatform = u.getViewingPlatform();
MultiTransformGroup multi = viewingPlatform.getMultiTransformGroup();
joyTransformGroup = multi.getTransformGroup(0);
vpTransformGroup = multi.getTransformGroup(1);
viewingPlatform.setNominalViewingTransform();
long initTime = System.currentTimeMillis();
bg = createSceneGraph();
System.out.println("Time taken to create
BranchGroup:"+(System.currentTimeMillis() - initTime));
u.addBranchGraph( bg );
}
public BranchGroup createSceneGraph() {
BranchGroup objRoot = new BranchGroup();
TransformGroup transGrp = new TransformGroup();
transGrp.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
transGrp.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
Transform3D t3d = new Transform3D();
Vector3f vec3f = new Vector3f(0.25f, -.50f, 0.0f);
t3d.setScale(0.1);
TransformGroup transGrp2 = new TransformGroup();
transGrp2.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
transGrp2.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
Transform3D t3d2 = new Transform3D();
Vector3f vec3f2 = new Vector3f(0.25f, -.50f, -2.0f);
t3d2.setScale(0.1);
for ( int i = 0; i <numbers; i++)
{
vec3f.set(i*0.25f, -.50f, -i*.1f);
t3d.setTranslation(vec3f);
TransformGroup trans1 = new TransformGroup(t3d);
trees[i] = new SharedTreeGeo();
trees[i].setTotalHt(8.f);
trees[i].setTrunkRad(.1f);
trees[i].calTrunkHt();
trees[i].setTopConeRatio(0.2f);
trees[i].setBottomConeRatio(0.3f);
trees[i].setCrownRad(1.f);
trees[i].buildTree();
Appearance appear = new Appearance();
Material material = new Material();
appear.setMaterial(material);
trans1.addChild(new Shape3D(trees[i].geoms, appear));
transGrp.addChild(trans1);
}
objRoot.addChild(transGrp);
// ground
QuadArray qArray = new QuadArray(4, GeometryArray.COORDINATES |
GeometryArray.COLOR_3|
GeometryArray.BY_REFERENCE);
Point3f gp1 = new Point3f(-7f, 0f, -50f);
Point3f gp2 = new Point3f(-7f, 0f, 4f);
Point3f gp4 = new Point3f(7f, 0f, -50f);
Point3f gp3 = new Point3f(7f, 0f, 4f);
float[] corrd = {gp1.x, gp1.y, gp1.z, gp2.x, gp2.y, gp3.z,
gp3.x, gp3.y, gp3.z, gp4.x, gp4.y, gp4.z};
Color3f c1 = new Color3f(0.6f, 0.6f, 0.6f);
Color3f c2 = new Color3f(0.6f, 0.6f, 0.6f);
Color3f c3 = new Color3f(0.6f, 0.6f, 0.6f);
Color3f c4 = new Color3f(0.6f, 0.6f, 0.6f);
float[] color = { c1.x, c1.y, c1.z, c2.x, c2.y, c2.z,
c3.x, c3.y, c3.z, c4.x, c4.y, c4.z};
qArray.setCoordRefFloat(corrd);
qArray.setColorRefFloat(color);
Appearance app = new Appearance();
Shape3D shape = new Shape3D(qArray, app);
Transform3D t = new Transform3D();
// move the object upwards
t.set(new Vector3f(0.0f, -.50f, 0.f));
t.setScale(0.1);
TransformGroup trans = new TransformGroup(t);
trans.addChild(shape);
objRoot.addChild(trans);
// behavior
OrbitBehavior orbit = new OrbitBehavior(vFstCanvas3D,
OrbitBehavior.REVERSE_ALL);
BoundingSphere bounds =
new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0);
orbit.setSchedulingBounds(bounds);
viewingPlatform.setViewPlatformBehavior(orbit);
// add light
Vector3f LightDirection = new Vector3f( 0.7f, -1.0f, -.4f );
DirectionalLight dirLight = new DirectionalLight( White, LightDirection );
dirLight.setInfluencingBounds( bounds );
objRoot.addChild( dirLight );
BoundingLeaf boundingLeaf = new BoundingLeaf( bounds );
objRoot.addChild( boundingLeaf );
// add backgroud
Background background = new Background();
background.setColor( bkground );
background.setCapability( Background.ALLOW_COLOR_WRITE );
background.setApplicationBoundingLeaf(boundingLeaf);
objRoot.addChild( background );
objRoot.compile();
return objRoot;
}
public static void main(String[] args) {
Tree3ele2 treeTest = new Tree3ele2();
new MainFrame(treeTest, 1024, 512);
}
}
Lan Wu-Cavener
Research Associate and Programmer
Dept. of Landscape Architecture