[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Image -> ElevationGrid PROTO implementation
casteeld wrote:
> Rikk Carey wrote:
>
> > Geez, it would be a heck of lot easier to write a tool to read an
> > image file and convert into an ElevationGrid node than to add more bloat
> > to the spec. I'm surprised that no one has done this by now.
>
> Anyone who has or can write a proto for this please send me a link.
I too am surprised. Since I'm in a hacking mood at the moment, let's
write one while answering this email.
Lemme see - It goes a little something like this.....
(turning up the music in the process)
---------------------------------
#VRML V2.0 utf8
#
# Standalone heightfield generator. Must send it values and it will
# generate the output in return. The output is an ElevationGrid node
# so you can ROUTE it directly to the geometry field of a Shape node.
#
# heightScale: is A scale of the height values (which are read from
# the image in values of 0-255
# createGrid: Send it the name of the file that you want to use as the
# source of the height field
# postSpacing: Use this to change the default distance of the height
# spacing will cause the image to be regenerated from scratch.
#
# Proto performs generation from the eventsProcessed method which
# allows you to change both the file url and the post spacing and only
# cause one regeneration of the output.
#
PROTO ImageHeightFieldGenerator [
field SFFloat heightScale 1.0
eventIn SFString createGrid
eventIn SFFloat postSpacing
eventOut SFNode newElevationGrid
eventOut SFInt32 error
]{
WorldInfo {
title "Image HeightField generator"
info [
"Written by Justin Couch 11th March 1998 "
"justin@vlc.com.au"
]
}
Script {
field SFFloat heightScale IS heightScale
eventIn SFString sourceUrls IS createGrid
eventIn SFFloat postSpacing IS postSpacing
eventOut SFNode gridOutput IS newElevationGrid
eventOut SFInt32 error IS error
url "EGridGen.class"
}
}
-----------------
/**
* Height Field from image generator
*
* Written by Justin Couch 1998 11th March 1998
* justin@vlc.com.au
*/
import java.awt.*;
import java.awt.image.*;
import java.net.URL;
import java.net.MalformedURLException;
import vrml.*;
import vrml.field.*;
import vrml.node.*;
import netscape.security.*;
public class EGridGen extends Script
{
// fields of the script
private static final String SCALE_FIELD = "heightScale";
// eventIns to the script
private static final String URL_EVENT = "sourceUrls";
private static final String POST_EVENT = "postSpacing";
// eventOuts to the script
private static final String GRID_OUTPUT = "newElevationGrid";
private static final String ERROR_OUTPUT = "error";
// A couple of the error conditions
private static final int SUCCESSFUL = 0;
private static final int FAILED_LOAD = -1;
private static final int CONVERT_ERROR = -2;
private static final int BAD_VRML_ERROR = -3;
// java versions of the eventIns/Outs
private SFNode grid_output;
private SFInt32 error_output;
private Browser browser;
// the url of the image to be loaded
private String image_url;
// The post spacing to work out the e grid
private float post_spacing = 1;
// how much to scale the 0->1 values of the pixel averaging
private float height_scale = 1;
// Component for media tracker etc
private Component component;
/**
* Initialise the script
*/
public void initialize()
{
grid_output = (SFNode)getEventOut(GRID_OUTPUT);
error_output = (SFInt32)getEventOut(ERROR_OUTPUT);
SFFloat height_field = (SFFloat)getField(SCALE_FIELD);
height_scale = height_field.getValue();
browser = getBrowser();
component = new Component() {}; // need an image observer
}
/**
* Process an event
*/
public void processEvent(vrml.Event e)
{
String event_name = e.getName();
if(event_name.equals(URL_EVENT))
{
ConstSFString value = (ConstSFString)e.getValue();
image_url = value.getValue();
}
else if(event_name.equals(POST_EVENT))
{
ConstSFFloat value = (ConstSFFloat)e.getValue();
post_spacing = value.getValue();
}
}
/**
* At the end of this event cascade. Do the generation
*/
public void eventsProcessed()
{
generateImage();
}
/**
* Lets generate the image from the current data
*/
private void generateImage()
{
// first, lets load the image.
Image img = null;
try
{
img = loadImage();
}
catch(AWTException e)
{
// this was generated by the media tracker
error_output.setValue(FAILED_LOAD);
return;
}
if(img == null)
{
error_output.setValue(FAILED_LOAD);
return;
}
// get the width and height
int width = img.getWidth(component);
int height = img.getHeight(component);
// Before we start, we need the pixel values
PixelGrabber pg = new PixelGrabber(img,
0, 0,
width, height,
true); // forced to RGB
try
{
boolean fetched = pg.grabPixels();
if(!fetched)
{
error_output.setValue(CONVERT_ERROR);
return;
}
}
catch(InterruptedException ie)
{
error_output.setValue(CONVERT_ERROR);
return;
}
// needed to convert pixel colours to heights
ColorModel cm = pg.getColorModel();
int i;
int pixel_value;
int red, green, blue; // ignore alpha
float height;
int[] pixels = (int[])pg.getPixels();
float[] heights = new float[pixels.length];
try
{
for (i = 0; ; i++)
{
pixel_value = pixels[i];
red = (pixel_value >> 16) & 0xff;
green = (pixel_value >> 8) & 0xff;
blue = (pixel_value ) & 0xff;
height = red + green + blue / (3 * 255);
// average of all three - greyscale
heights[i] = height * height_scale;
}
}
catch(ArrayIndexOutOfBoundsException aie)
{
// loop termination condition
}
// So now we just put together a string that is the VRML
// ElevationGrid
StringBuffer buffer = new StringBuffer("ElevationGrid { ");
buffer.append("xDimension ");
buffer.append(width);
buffer.append(" zDimension ");
buffer.append(height);
buffer.append(" xSpacing ");
buffer.append(post_spacing);
buffer.append(" zSpacing ");
buffer.append(post_spacing);
// now the fun stuff - the height field!
buffer.append(" height [ ");
try
{
for(i = 0; i < pixels.length; i++)
{
buffer.append(heights[i]);
buffer.append(" ");
}
}
catch(ArrayIndexOutOfBoundsException aie)
{
// loop termination condition
}
buffer.append(" ] }");
Node[] grid_node = null;
// create this as some VRML
try
{
grid_node =
(Node[])browser.createVrmlFromString(buffer.toString());
}
catch(InvalidVRMLSyntaxException e)
{
// shit!!!
error_output.setValue(BAD_VRML_ERROR);
return;
}
// Now that this is done, send it as the eventOut!!!
grid_output.setValue(grid_node[0]);
}
/**
* Attempt to load the image and turn it into java version
*/
private Image loadImage()
throws AWTException
{
MediaTracker mt;
Image img;
Toolkit toolkit;
try
{
// if we are running inside netscape you will need these
// next two lines
PrivilegeManager.enablePrivilege("UniversalConnect");
PrivilegeManager.enablePrivilege("UniversalFileAccess");
URL url = new URL(image_url);
if (url == null)
{
System.out.println("Unable to open file - wrong path");
return null;
}
toolkit = Toolkit.getDefaultToolkit();
img = toolkit.getImage(url);
mt = new MediaTracker(component);
try
{
mt.addImage(img, 0);
mt.waitForAll();
}
catch (InterruptedException e)
{
}
try
{
if (mt.isErrorAny())
{
if (mt.isErrorAny())
throw new AWTException("error loading image "
+ img.toString());
}
}
catch(AWTException e)
{
System.err.println("Error loading image " +
img.toString());
return null;
}
// just to be safe!
PrivilegeManager.disablePrivilege("UniversalConnect");
PrivilegeManager.disablePrivilege("UniversalFileAccess");
mt = null;
return img;
}
catch(MalformedURLException ex)
{
System.out.println("Malformed URL");
return null;
}
catch(ForbiddenTargetException e)
{
// oh well. Shit happens
return null;
}
}
}
------------------------------
See, that wasn't too scary was it?....
OK, so I wrote it in the email here on the fly. I don't guarantee
anything. I know that at least it compiles barring cut & paste bugs (OK
kiddies - let's see you code that one in ECMAScript!) but as for running
- who knows?
The biggest bug that you may have to sort out is making sure that the
width of the ElevationGrid matches the width of the image (and not the
length), and that the values you are placing in the image are the
correct way around for the elevation grid (both are single dimension
arrays for a 2D object - but have they represented themselves the same
way?). Should be fairly trivial to sort out.
It will only handle the basic image types that are supported by Java -
namely GIF and JPEG.
I would be quite happy to work on this stuff if someone wanted me to
continue development, but that would take some other sort of incentive
at this point in time. For example, a version that uses a pixel texture
or other image types (I have got a BMP decode running around so I could
also include that here too), threaded so that it doesn't kill the event
model (which it does at the moment! - everything will hang until this
image is loaded and generates an eventOut - error or node).
Good luck. I'll be back!
--
Justin Couch
Senior Software Engineer VRML-Java Terminator
ADI Ltd, Systems Group.
justin@vlc.com.au http://www.vlc.com.au/~justin/
-------------------------------------------------------------------
"Look through the lens, and the light breaks down into many lights.
Turn it or move it, and a new set of arrangements appears... is it
a single light or many lights, lights that one must know how to
distinguish, recognise and appreciate? Is it one light with many
frames or one frame for many lights?" -Suocomandante Marcos
-------------------------------------------------------------------
-------------------------------------------------------------------------
for list subscription instructions,
send email to www-vrml-request@vrml.org with text "info"