We can't let Justin write all that lot without joining in can we :)
I think Justin has given a very balanced and fair appraisal of how J3D
measures up (although some of what he said is beyond my experience in
which case I'm not qualified to judge). Here are some of my own thoughts
(for what they're worth) and I'd be happy for others to take issue with
any part they disagree with. In case I offend anyone with my comments
please accept that this is just my candid opinion of the 'lie of the
land' and nothing more.
To my mind Java3D represents a tremendous opportunity to take 3D
graphics programming to a new level of usability and power by
abstracting much of the underlying rendering and scene management from
the programmer. In fact I'd agree with just about everything the Java3D
marketing blurb will have you believe if you go and read it on Sun's Web
site. Multi-platform support is simply a bonus as far as I'm concerned.
The issue with J3D in my opinion therefore is not so much whether it has
potential, but whether it will ever reach that potential through proper
development of the API, and whether the various implementations of that
API will ever achieve a level of stability and commercial robustness
which will encourage big-budgeted developers to jump on board. If you
have ever programmed Java's AWT or Swing graphical front ends you'll
know why Java is gradually dying a death on the client side: although
the APIs are very comprehensive and powerful and give that all-important
cross-platform portability, in their implementation things have
faltered, and no matter how hard you try it's extremely difficult to
make a commercial-quality GUI from these components.
I came to Java3D from the Java programming side rather than as a 3D
developer and my immediate impression from fiddling around with the API,
reading Sun's docs and official books, and browsing through the
published source code in the j3d.utils package, is that most of the
Java3D team have come from a 3D background, where their experience has
been with OpenGL, VRML, etc. As such the J3D API is markedly different
from other Java APIs and any seasoned Java programmer will take some
time to adjust to it. Most notably, for example, the API pretty well
ignores the whole concept of JavaBeans, which is somewhat surprising
when you consider that this is the cornerstone enterprise technology
which Sun has built Java's future upon.
There are hardly any interfaces in the API - almost all abstractions
have been implemented as abstract base classes which don't give the same
future development potential as interfaces would have done, and also
makes it hard to create higher-level objects from a combination of
classes. So I think the API would have been better off in a number of
cases by having an interface and an accompanying default interface
implementation. I'm sure others would say that this is a pedantic point
of little concern.
Once you become familiar with the quirks of the API it's easy to use and
I'd guess anyone coming from VRML or OpenGL would pick it up very
quickly. There are two main areas however which I think are a
fundamental weakness in the J3D API, and I'd be interested to know what
others think about them.
First, in order to be able to optimize parts of the scene graph, J3D
uses a 'capability bit' scheme so that objects which the programmer
wants to modify in a live scene can be flagged. Look in almost any API
class and you'll find a whole set of capability bits which you can set
or unset for objects of that class. I've no idea of the magnitude of the
performance gains which can be made by optimizing objects using these
settings (I'm assuming they are quite significant) but what I do know is
that enabling and disabling these bits is a complete curse. I can't
understand why it's not possible to pass a capability mask to these
classes to set multiple capabilities in a single method call. Then you'd
be able to do something like:
private static final int EDITOR_MODE = ALLOW_TRANSFORM_READ |
ALLOW_TRANSFORM_WRITE |
ALLOW_CHILDREN_EXTEND |
ALLOW_CHILDREN_READ;
...and so on.
The result of this capability scheme is that writing a J3D editor is
made much harder than you would expect, and I'd guess that the lack of
powerful J3D editors is one reason why developers aren't taking so
readily to this API.
The second weakness is perversely one that ought to be Java3D's greatest
strength, the killer punch, the one that blows the opposition out of the
water and has developers queuing at the door: serialization.
You can look around at various 3D file formats and uhm and ah at which
one is best, but there is nothing out there that even comes close to
matching the power of the Java .class file. With this file format you
can do pretty well anything, and because Java classes can be described
in terms of interfaces, object files need only implement that interface
to be readable in any Java-aware editor that's ever written, now or in
the future. The classloader does all the work for you, and through this
mechanism you can load anything from a single Point2d to a whole
universe. Coupled with Java's Reflection and Beans APIs, it's possible
to discover the (public) interfaces of a serialized object without it
ever being published.
To give a concrete example of this power, take texture loading, as
there's been a fair amount of discussion about this recently on this
newsgroup.
The messages I've seen suggest that the majority of J3D users out there
are using the j3d.utils.image.TextureLoader package to load textures
into their app. Although this class is fine for testing light demo stuff
in J3D I'd argue that for larger 3D apps you require texture loading
routines which go beyond the capabilities of this class.
The basic problem with TextureLoader (as used by most users) is that it
is using the AWT push model for image acquisition. This model harks back
to when Java first came on to the scene - the need to load a number of
resources (usually Web-based) from across a network into a client
application (specifically a browser). When you call an image from AWT
you get an object reference for that image, but not the image itself.
The VM starts a separate thread to load the image data for you from the
specified file/url, and your main app can get on with other things (like
loading the text of an HTML document). Using the ImageObserver interface
you are able to monitor the loading state of this image, but you have no
direct control over the loading.
As Justin (who knows far more about this than I do) has pointed out, the
way this image loading mechanism is implemented in Sun's VM is slow and
has serious drawbacks, but it's fine when you have only a few Web images
to load, or you want some icons for your Swing buttons at the start of
your application. However, if you are authoring a very large 3D world
which has potentially thousands of textured objects then this push model
is wholly inappropriate. Textures need to be constantly streamed from
disk at high speed and there is no way that the TextureLoader class can
ever cope with such a requirement.
Because the image is loading in a separate thread TextureLoader has no
idea when the image will be fully loaded. It has to suspend the current
thread, waking up intermittently to see if the image has loaded yet. So
you get code in TextureLoader like this (I've put it as psuedo code):
while (true) {
check the loading state of the image
if (the image has loaded or an error has been thrown) {
break;
} else {
Thread.sleep() for 100 milliseconds
}
}
The Thread.sleep() call is an absolute killer. 100 milliseconds on one
of today's 500Mhz machines is close to forever, and this is just for
*one* image.
Justin uses his own native library to load images which I imagine is
very fast. Chris Wewerka pointed out another method of loading textures
(via TextureLoader) - by using the Java Advanced Imaging API, which has
the advantage of being able to load TIFF, PNG and BMP images as well as
JPEGs and GIFs. However when we tested the JAI some months ago we were
really disappointed with the image loading performance of the codecs -
they supposedly use native calls but images still seem to take an
inordinate amount of time to load.
The way we eventually approached this issue was the same way we've
approached every issue we've ever dealt with in optimising Java apps -
get the data in a form that Java can handle at speed. Java may be slow
in certain respects, but at some things it's darned quick due to
underlying native implementations, and loading bytestreams is one of
them.
We looked at our 3DSMax texture library and realised that although we
often edit *materials* in Max, we very rarely ever edit *bitmaps* (in
fact it's a rule that we can't edit bitmaps without good reason, simply
because we may 'break' someone else's material). Therefore when we
export Max meshes or scenes into our J3D editor there is no reason why
we can't convert the bitmaps into a serialized form that Java will be
able to handle efficiently, and without using AWT calls (there's nothing
new in this of course - other object file formats store image data in
this form).
We convert the bitmaps using JAI and from then on it's just a case of
loading a serialized Java .class file, which has all the J3D Texture
properties as well. The conversion process optimizes the image file
based on the colors/alpha it contains, and we also have the advantage
that we can combine two Max submaps - the diffuse and opacity maps -
into a single J3D texture file.
The upgrade in peformance from using these serialized Textures is
considerable. On a PII 450Mhz machine with a very ordinary HD we are
loading textures into J3D as fast as the HD can deliver them - at an
equivalent rate of 15MB/sec, and this is in 100% Java (apart from J3D of
course). In other words we're loading images faster than you can get
them into Photoshop or Paint Shop Pro.
However, it's not so much the performance aspect of serialization which
I believe is where J3D's true potential lies, but in the ability to hold
3D objects as JavaBean or Corba-type entities. Objects can be built
which hold multiple Shape3Ds, Appearances, animations and AI behaviors
all in the same class file (or Jar file if you want to group several
classes). Stick such an object in the J3D equivalent of the BeanBox and
let Java's reflection API do the rest.
I'm in danger of digressing here, so I'll return to the original point:
serialization is Java's ace-in-the-hole. Serialization of Java3D objects
is easy to implement (we build everything in 3DSMax but once tweaked in
our J3D editor we serialize it out, be it a humble lonely sphere or a
large scene graph). Java3D does not directly support serialization
however so you have to do it through the back door, and by using
placeholder classes instead of extending Java3D classes. This is a big
weakness in my opinion, and one which I hope will be addressed in a
future release.
As for object loaders, we use the same one for everything, even
universes. It's 16 lines long.
Turning to Sun's current J3D implementation, I think things are going
pretty much as you'd expect for a relatively new library - some stuff
works really nicely, other parts are as buggy as hell. However, I share
Justin's concern at the lack of new implementation releases. The 3D
world is a fast moving one and we don't seem to be seeing much progress
at the moment. I'd guess that J3D isn't high on Sun's list of
priorities, and we don't know what meagre resources may have been dished
out to the Java3D team. Certainly I believe that if the source was
publicly released then the whole thing would move forward at a much
greater pace than it is now. I don't say this out of any delusion that
we can program better than Sun's programmers; I simply believe that
everyone's outlook would change overnight. While Sun is playing its
source cards so close to its chest then so is everyone else. If it was
Open Source then all of a sudden we'd all pile in with our own
proprietary code and optimizations.
I wonder however whether the faltering development progress is due at
least in part to political factors - every time some new big name
licenses J3D the Sun team are distracted from their main efforts through
having to port a platform implementation for the new licensee. If this
is the case then you have my sympathy guys.
At my end the lack of updates is putting pressure to find alternative
solutions and we've just started looking at OpenGL/Java bindings, though
only tentatively. Personally this is the last road I'd want to go down.
The issue regarding Java3D not releasing objects for clean-up (read:
memory leaks) is a very serious one, as is the non-release of texture
memory. Both problems make programming anything other than static scene
graphs or small demos pretty futile. You could never release a
commercial editor at present for example.
Java3D also has a problem working with other apps/threads at the moment,
which I hope is going to be sorted out some time. Once started, the J3D
renderer just carries on rendering in a permanent loop (technically this
isn't so, but practically it is because one tends to have either
interpolators or per-frame behaviors operating in the scene graph).
Unfortunately the various J3D threads are operating uncontrollably, i.e.
there's no brake on their CPU usage. Therefore if you create a scene
with a completely stationary cube in it and add a KeyNavigator behavior
(sending the renderer into an indefinite loop) then the renderer will
merrily race along renderering a completely static scene at 500
frames/sec and above. If ever you want to see a complete waste of good
CPU time, look no further.
The J3D renderer is set at priority 5, as is the J3D behavior thread, so
if you have another thread in your app running at a lower priority (this
would be typical for background file loaders or threads building a new
scene graph in preparation for an avatar moving into a new area) then
this thread is starved of CPU time due to the J3D renderer hogging it
all. The lower priority thread may get *some* time, but not enough to
run efficiently.
Okay, I hear you say, just up the priority of the background thread to 5
and it'll get shared time with the renderer. Well, for a start this
might not be what you want. A background thread is usually a background
thread for good reason. Secondly, the JLS doesn't specify (to my
knowledge) any required behavior for equal-priority threads, so on a
cooperatively multithreaded system there's no guarantee that setting the
background thread to 5 will fix things. Lastly however, even with
preemptive multithreading, you don't get what you want. If you run a
thread with the same priority as the J3D renderer an interesting thing
happens: the frame rate suddenly drops to 1/20th or so of it's previous
level.
I don't know what's causing this but I'd hazard a guess: J3D has it's
own background threads running at a priority of less than 5, but they're
necessary to the completion of each frame. As soon as another
application thread is started at priority 5, J3D's sub-threads become
starved of CPU time, causing the renderer to crawl like a snail.
This threading problem isn't the J3D team's fault. The renderer
obviously needs some sort of control on it (such as a
setRendererMaximumFrameRate() method) but this isn't really practicable
with the current resolution of the System clock (on Win32). The
System.currentTimeMillis() call on Win32 will only get you a resolution
of around 55 millis or so, which is just not good enough for real time
simulations. So, if you try and clock the renderer to achieve a sensible
frame rate you'll probably end up with an unacceptable frame rate of
around 15 fps, and worse, it'll be jerky.
J3D therefore badly needs a better clock. Windows has I believe at least
two clocks with far better resolution than the clock used by the JVM,
and since Java3D is non-core, maybe it would be possible for J3D to make
a native call to one of these clocks instead. This would also be a big
boost for developers trying to incorporate animations into their
applications. (I don't know what it's like on Solaris - I expect there's
already a good clock on that platform.)
Whatever, as soon as the renderer can be halted even for just a few
millis, mixing multithreaded apps with Java3D is going to be far easier.
Final wishlist:
1) Multitexturing: An absolute must if J3D wants to be up there with the
best. 3DSMax3 now supports up to 100 vertex channels per object and at
the recent developers' conference the stuff that was shown using real
time MT was breathtaking.
2) A seemingly minor one: Intel has put Perlin's noise function into the
MMX instruction set. I really wish we had access to a native version of
this in J3D. It's got uses everywhere - terrain generation, procedural
texturing, animation controllers. Never was there so much power in so
little code.
As a final point, I ought to mention that what you've read above is
mainly a 'problems with J3D' treatise, which is what you inevitably get
from developers if you ask their opinion of how this-or-that API is
going. I must stress that Java3D has some really excellent capabilities
and above all is backed by the power of the Java language. For some
reason much of the marketing of J3D has been directed at small Applet
programming (read the 'Ready-to-Run Java 3D' book and that's *all*
you'll think it's good for) whereas in reality Java3D has the potential
to be the foundation API for the construction of very large multiuser
worlds.
Java's network programming library is so powerful, so simple to use,
that the combination of fast Java-based multithreaded servers linking to
Java3D on the client is no longer a pipe-dream. You can code so quickly
in Java, and your programs are so solid, that if J3D can at least keep
the opposition in sight in the immediate term, then in due course I have
no reason to believe it can't overtake it.
The big question is, can it stay the pace?
Andrew Moulden
===========================================================================
To unsubscribe, send email to [EMAIL PROTECTED] and include in the body
of the message "signoff JAVA3D-INTEREST". For general help, send email to
[EMAIL PROTECTED] and include in the body of the message "help".