package COM.furryandfrisky.vrml;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Vector;
import
COM.furryandfrisky.landscape.FractalLandscapeGenerator;
/*
* VRML landscape generator.
*/
//----------------------------------------------------------------------------
public class VrmlLandscapeGenerator
{
private static String usage()
{
return "usage: java COM.furryandfrisky.vrml.VrmlLandscapeGenerator ";
}
/**
* Create a vrml file.
*
* @param arg[0] the scale of the landscape
* @param arg[1] the random seed for the generator
* @param arg[2] the name of the output file
*/
public static void main( String[] args )
{
if( args.length != 3 )
{
System.out.println( usage() );
System.exit( 0 );
}
String out_file_name = args[2];
if( ! out_file_name.toLowerCase().endsWith( ".wrl" ))
out_file_name += ".wrl";
try
{
int scale = Integer.parseInt( args[0] );
long seed = Long.parseLong( args[1] );
OutputStream out_stream = new FileOutputStream( new File( out_file_name ));
VrmlLandscapeGenerator generator = new VrmlLandscapeGenerator( scale, seed, out_stream );
generator.start();
out_stream.close();
System.out.println( "finished" );
}
catch( IOException e )
{
System.out.println( "\ncan not open file " + out_file_name + " for writing." );
System.out.println( usage() );
}
catch( NumberFormatException e )
{
System.out.println( "\ninvalid number format - " + e.getMessage() );
System.out.println( usage() );
}
catch( IllegalArgumentException e )
{
System.out.println( "\n" + e.getMessage() );
System.out.println( usage() );
}
System.exit( 0 );
}
/**
* Create a new generator.
*
* @param scale scaling factor for the landscape, valid values 2 - 10
* @param seed initial random seed value
* @param output the destination output stream for the vrml file
*/
public VrmlLandscapeGenerator( int scale, long seed, OutputStream outStream )
{
if( scale < 2 || scale > 10 )
throw new IllegalArgumentException( "invalid scale - " + scale );
output = outStream;
if( ! (output instanceof BufferedOutputStream ))
output = new BufferedOutputStream( output );
generator = new FractalLandscapeGenerator( scale, seed );
colors = new Vector();
lowest_point = 0.0f;
tiny = generator.getSize() / 80.0f;
adjust = (float) (10000 / Math.pow( 2, generator.getScale() - 2));
int num_facets = (int) Math.pow( generator.getSize() -1, 2 ) * 4;
highest = new int[num_facets];
color_index = new int[num_facets];
}
/**
* Generate the landscape and write the equivalent vrml file to the output
* stream.
*/
public void start()
{
generator.generate();
print_header();
print_cameras();
print_setup();
print_coordinates();
print_colors();
print_facets();
print_sea();
try
{
output.flush();
}
catch( IOException e )
{
}
}
/**
* Print a string to the output stream.
*/
private void out( String text )
{
if( output == null )
return;
try
{
text += "\n";
byte[] out_buff = new byte[text.length()];
text.getBytes( 0, text.length(), out_buff, 0 );
output.write( out_buff );
}
catch( IOException e )
{
System.err.println( "IO Error - " + e.getMessage() );
output = null;
}
}
/**
* Print any vrml header info.
*/
private void print_header()
{
out( "#VRML V1.0 ascii" );
out( "\n# Generated by VrmlLandscapeGenerator - version " + version );
out( "# Random seed = " + generator.getSeed() );
out( "# scale = " + generator.getScale() + "\n" );
}
/**
* Print the camera angles on the landscape. North, south, east, west and
* top.
*/
private void print_cameras()
{
out( "DEF Camera Switch {" );
out( "\twhichChild 0" );
out( "\tDEF northern PerspectiveCamera {" );
out( "\t\tposition " + "0.0 " + (generator.getSize()/2.0f) + " " + ((generator.getSize() -1) * 3.0f) );
out( "\t\theightAngle 1.13" );
out( "\t}" );
out( "\tDEF eastern PerspectiveCamera {" );
out( "\t\tposition " + (-(generator.getSize()-1) * 3.0f) + " " + (generator.getSize()/2.0f) + " 0.0" );
out( "\t\torientation 0.0 1.0 0.0 -1.57" );
out( "\t\theightAngle 1.13" );
out( "\t}" );
out( "\tDEF southern PerspectiveCamera {" );
out( "\t\tposition " + "0.0 " + (generator.getSize()/2.0f) + " " + (-(generator.getSize()-1) * 3.0f) );
out( "\t\torientation 0.0 1.0 0.0 3.14" );
out( "\t\theightAngle 1.13" );
out( "\t}" );
out( "\tDEF western PerspectiveCamera {" );
out( "\t\tposition " + ((generator.getSize()-1) * 3.0f) + " " + (generator.getSize()/2.0f) + " 0.0" );
out( "\t\torientation 0.0 1.0 0.0 1.57" );
out( "\t\theightAngle 1.13" );
out( "\t}" );
out( "\tDEF top PerspectiveCamera {" );
out( "\t\tposition " + "0.0 " + ((generator.getSize()-1) * 2.0f) + " 0.0" );
out( "\t\torientation 1.0 0.0 0.0 -1.57" );
out( "\t\theightAngle 1.13" );
out( "\t}" );
out( "}" );
}
/**
* Setup the background color, light source, and shape hints.
*/
private void print_setup()
{
out( "DEF BackgroundColor Info {" );
out( " string \"0.0 0.0 0.0\"" );
out( "}" );
out( "Translation {" );
out( "\ttranslation " + -((float) generator.getSize()-1) + " 0.0 " + -((float) generator.getSize()-1) );
out( "}" );
out( "DirectionalLight {" );
out( " on TRUE" );
out( " direction 1.0 -0.8 0.0" );
out( "}" );
out( "ShapeHints {" );
out( " creaseAngle 0.8" );
out( "}" );
}
/**
* Print all the points in the landscape.
*/
private void print_coordinates()
{
out( "Coordinate3 {" );
out( " point [" );
int size = generator.getSize();
out( "# landscape points" );
// landscape
for( int i = 0; i < size; i++ )
{
for( int j = 0; j < size; j++ )
{
float height = generator.getHeightAt( i, size-1 - j ) / adjust;
out( "\t" + i * 2 + " " + height + " " + j * 2 + "," );
if( height < lowest_point )
lowest_point = height;
}
}
lowest_point -= tiny;
out( "# interpolated points" );
// interpolated points
int highest_index = 0;
for( int i = 0; i < size - 1; i++ )
{
for( int j = 0; j < size -1; j++ )
{
int u_left = generator.getHeightAt( i, size-1 - j );
int b_left = generator.getHeightAt( i, size-1 - (j+1) );
int u_right = generator.getHeightAt((i+1), size-1 - j );
int b_right = generator.getHeightAt((i+1), size-1 - (j+1) );
int middle_height = (u_left + u_right + b_left + b_right) / 4;
out( "\t" + ((i * 2) + 1) + " " + (middle_height / adjust) + " " + ((j * 2) + 1) + "," );
highest[highest_index++] = max( u_left, b_left, middle_height );
highest[highest_index++] = max( u_left, u_right, middle_height );
highest[highest_index++] = max( u_right, b_right, middle_height );
highest[highest_index++] = max( b_left, b_right, middle_height );
}
}
out( "# base points" );
// base points
int width = (size - 1) * 2;
for( int i = 0; i <= width; i += 2 )
out( "\t" + "0 " + lowest_point + " " + i + "," );
for( int i = 0; i <= width; i += 2 )
out( "\t" + width + " " + lowest_point + " " + i + "," );
for( int i = 0; i <= width; i += 2 )
out( "\t" + i + " " + lowest_point + " 0," );
for( int i = 0; i <= width; i += 2 )
out( "\t" + i + " " + lowest_point + " " + width + "," );
out( " ]" );
out( "}" );
}
/**
* Print the colors need for the landscape.
*/
private void print_colors()
{
for( int i = 0; i < highest.length; i++ )
add_color( i, generator.getColorAt( highest[i] ));
out( "Material {" );
out( " diffuseColor [" );
for( Enumeration e = colors.elements(); e.hasMoreElements(); )
out( "\t\t" + color_string( ((Integer) e.nextElement()).intValue() ) + "," );
out( "# edge color" );
// edges
out( "\t\t" + color_string( 0xFF505050 ) + "," );
out( " ]" );
out( "}" );
out( "MaterialBinding {" );
out( " value PER_FACE_INDEXED" );
out( "}" );
}
/**
* Print all the facets.
*/
private void print_facets()
{
out( "IndexedFaceSet {" );
out( " coordIndex [" );
int size = generator.getSize();
int last = size * size;
out( "# terrain facets" );
for( int i = 0; i < size - 1; i++ )
{
for( int j = 0; j < size - 1; j++ )
{
int index = (i * size) + j;
int middle = index + (last - i);
out( "\t" + index + "," + (index + 1) + "," + middle + ",-1," );
out( "\t" + index + "," + middle + "," + (index + size) + ",-1," );
out( "\t" + (index + size) + "," + middle + "," + (index + size + 1) + ",-1," );
out( "\t" + (index + size + 1) + "," + middle + "," + (index + 1) + ",-1," );
}
}
out( "# base facets" );
int base_index = (size * size) + ((size - 1) * (size - 1));
for( int i = 0; i < size -1; i++ )
out( "\t" + (base_index +i) + "," + (base_index +i+1) + "," + (i+1) + "," + i + ",-1," );
int offset = size*size - size;
for( int i = 0; i < size -1; i++ )
out( "\t" + (base_index +size+i+1) + "," + (base_index +size+i) + "," + (offset +i) + "," + (offset +i+1) + ",-1," );
for( int i = 0; i < size -1; i++ )
out( "\t" + (base_index +2*size+i+1) + "," + (base_index+2*size+i) + "," + (size*i) + "," + (size*(i+1)) + ",-1," );
for( int i = 0; i < size -1; i++ )
out( "\t" + (base_index +3*size+i) + "," + (base_index+3*size+i+1) + "," + (size*(i+2)-1) + "," + (size*(i+1)-1) + ",-1," );
out( "\t" + (base_index+size-1) + "," + base_index + "," + (base_index+size*3-1) + "," + (base_index+size*2-1) + ",-1," );
out( " ]" );
out( " materialIndex [" );
out( "# terrain material binding" );
for( int i = 0; i < color_index.length; i++ )
out( "\t\t" + color_index[i] + "," );
out( "# base material binding" );
for( int i = 0; i < 4*(size-1)+1; i++ )
out( "\t\t" + (colors.size()) + "," );
out( " ]" );
out( "}" );
}
/**
* Print a blue box for the sea.
*/
private void print_sea()
{
int size = generator.getSize();
out( "# the sea" );
out( "Separator {" );
out( "\tMaterial {" );
out( "\t\tdiffuseColor " + color_string( FractalLandscapeGenerator.SEA ) );
out( "\t}" );
out( "\tTranslation {" );
out( "\t\ttranslation " + (size-1) + " " + (-((0.0 - lowest_point) - tiny/2)/2) + " " + (size-1) );
out( "\t}" );
out( "\tCube {" );
out( "\t\twidth " + (((size-1) * 2) - tiny) );
out( "\t\tdepth " + (((size-1) * 2) - tiny) );
out( "\t\theight " + ((0.0 - lowest_point) - tiny/2));
out( "\t}" );
out( "}" );
}
/**
* Add a color for an indexed facet.
*/
private void add_color( int facet, int color )
{
Integer col = new Integer( color );
int index = colors.indexOf( col );
if( index != -1 )
color_index[facet] = index;
else
{
// only add unique colors
colors.addElement( col );
color_index[facet] = colors.size() - 1;
}
}
/**
* Maximum of 3 integers.
*/
private static int max( int h1, int h2, int h3 )
{
int h = (h1 > h2) ? h1 : h2;
return (h > h3) ? h : h3;
}
/**
* Get the floating point color value.
*/
private static String color_string( int color )
{
int red = color >> 16 & 0xFF;
int green = color >> 8 & 0xFF;
int blue = color >> 0 & 0xFF;
return (red / 255.0f) + " " + (green / 255.0f) + " " + (blue / 255.0f);
}
//-------------------------------------------------------------------------
private static final double version = 2.0;
private int[] highest;
private float tiny;
private int[] color_index;
private Vector colors;
private float adjust;
private float lowest_point;
private OutputStream output;
private FractalLandscapeGenerator generator = null;
}