Tim Bray wrote: > I have neat-looking haystack-y objects which use LineArray geometry. I hesitate to post this because it's such a hack, but it does demonstrate lighting lines. Attached is source for a class, HaystackBG, that uses lit line segments (LineArray) to simulate (vaguely) a haystack. The line segments are arranged such that one end is "on the ground" on a circular perimeter and the other is at the apex of the "haystack". Normals are assigned to each endpoint of the line segments that generally point perpendicular to the shape of the haystack (roughly a cone). I also had to insert a white cone inside the haystack (of slightly smaller size) so that the "back facing" lines are obscured--they can't be culled (or have their normals flipped) because that is done using PolygonAttributes. Besides, a line segment can't have a CCW winding order with just two vertices. Also attached is a small GIF image of the haystack (using a lousy Material) lit with white ambient and directional lights. Feedback (+ or -) welcome. -- Allen L. McPherson Member Holstein Aerobatic Club Los Alamos National Laboratory Scientific Viz / Advanced Computing Lab Box 1663 Group:CIC-ACL MS:B287 [EMAIL PROTECTED] Los Alamos, NM 87545 Voice: (505) 665-6548 Fax: (505) 665-4939 [Personal mail to: [EMAIL PROTECTED]]
// Copyright (c) 1999 by Allen Lynn McPherson. All rights reserved. // This example is provided WITHOUT ANY WARRANTY either expressed or implied. import java.util.Random; import javax.media.j3d.*; import javax.vecmath.*; // Fake (i.e. hack) "haystack" using lit lines. public class HaystackBG extends BranchGroup { public HaystackBG(float radius, float height) { // This is the haystack created using lit lines. this.addChild(new Haystack(radius, height, 500)); // Here we insert a white, unlit cone into the middle of the // haystack because "backfacing" lines can't be culled // and must be obscured. this.addChild(new InsertCone(radius-0.1f, height-0.1f, 30)); } // The haystack itself. private class Haystack extends Shape3D { public Haystack(float radius, float height, int nLines) { Point3f coords[] = new Point3f[2*nLines]; Vector3f norms[] = new Vector3f[2*nLines]; double range = 2 * Math.PI; double rIncr = range / nLines; double start = 0; float fScale = 0.3f; // Fudge scale double aTop = Math.PI/2 - Math.atan(height/radius); double theta = start; Random rand = new Random(); for (int i=0; i<nLines; i++) { // True point (before fudge) Point3f pt = new Point3f((float)(radius*Math.cos(theta)), 0, (float)(radius*Math.sin(theta))); // Fudge the true point for noise float xFudged = (rand.nextFloat() - 0.5f) * fScale; float yFudged = (rand.nextFloat() - 0.5f) * fScale; float zFudged = (rand.nextFloat() - 0.5f) * fScale; coords[2*i] = new Point3f(pt.x+xFudged, pt.y+yFudged, pt.z+zFudged); coords[2*i+1] = new Point3f(xFudged, height+yFudged, zFudged); // True vector (before fudge) Vector3f vec = new Vector3f(coords[2*i].x, (float)(radius * Math.tan(aTop)), coords[2*i].z); // Fudge the true vector for noise. xFudged = vec.x + (rand.nextFloat() - 0.5f) * fScale; yFudged = vec.y + (rand.nextFloat() - 0.5f) * fScale; zFudged = vec.z + (rand.nextFloat() - 0.5f) * fScale; norms[2*i] = new Vector3f(xFudged, yFudged, zFudged); norms[2*i].normalize(); norms[2*i+1] = new Vector3f(norms[2*i]); theta += rIncr; } LineArray lines = new LineArray(2*nLines, GeometryArray.COORDINATES | GeometryArray.NORMALS); lines.setCoordinates(0, coords); lines.setNormals(0, norms); Appearance app = new Appearance(); app.setMaterial(new Material( new Color3f(0.44f, 0.16f, 0.16f), // ambient new Color3f(0.0f, 0.0f, 0.0f), // emisive new Color3f(0.44f, 0.16f, 0.16f), // diffuse new Color3f(0.6f, 0.6f, 0.6f), // specular 128.0f * 0.3f)); // shininess app.setLineAttributes( new LineAttributes(3.0f, LineAttributes.PATTERN_SOLID, true)); this.setGeometry(lines); this.setAppearance(app); } } // Cone that is inserted into the center of the haystack to obscure // the "backfacing" lines (since they can't be culled). private class InsertCone extends Shape3D { Point3d coords[]; Vector3f norms[]; public InsertCone(float radius, float height, int sides) { coords = new Point3d[sides+2]; // Includes center point // Load coordinate array. coords[0] = new Point3d(0.0, height, 0.0); // Top center point for (int i=1; i<sides+2; i++) { // Make sure to build fan using CCW winding order for culling. double angle = (sides+2-i) * (2*Math.PI) / sides; coords[i] = new Point3d(radius * Math.cos(angle), 0.0, radius * Math.sin(angle)); } // Create traingle fan for geometry int counts[] = {coords.length}; TriangleFanArray fan = new TriangleFanArray(coords.length, GeometryArray.COORDINATES, counts); fan.setCoordinates(0, coords); Appearance app = new Appearance(); ColoringAttributes ca = new ColoringAttributes(1.0f, 1.0f, 1.0f, ColoringAttributes.FASTEST); app.setColoringAttributes(ca); this.setGeometry(fan); this.setAppearance(app); } } }