Monday, August 1, 2011

Exploration of 3d APIs In Java (As of August 2011)

I have always had an interest in the concept of 3d. In the past I've programmed simple environments in both OpenGL and Direct3d, and I even wrote a simple software 3d renderer in BASIC on a Tandy 102 years ago (family vacation growing up; way too many hours in that van). Recently, I've become interested in the idea of bringing 3d to my Java projects, so I've been exploring the options. Here's what I've found so far:


JavaFX

This is definitely the new kid on the block. JavaFX seems to think of itself as a superset of what Flash / Silverlight can do. You can code in pure java, and import your existing java business logic into JavaFX applications. It does run its own rendering, so you could see it as an alternative to Swing / AWT.

Although it's a potentially promising technology, the 3d support is very basic at this point, and not very practical. For example, primitives (such as cubes) are a feature they *may* add for the 2.0 release. (Richard Bair's June 4 reply here.) Plus, embedding it in Swing applications is more of a hack than an available feature, which makes integrating with an existing application a little difficult (and the way to do so may change with future releases).


Java 3D

This is the old mainstay which is used by a number of applications and toolkits. Java 3d has its own container, JCanvas3D, which makes it more difficult to integrate with existing swing component based layouts (though not impossible). It is a scene-graph based library. This means that you create java objects that contain 3d geometry and link them together; then Java 3d does the work to make the scene render. This is very different than a low level library, like OpenGL, where you control the rendering yourself.


JOGL

This is a community project called Java OpenGL, and is a Reference Implementation of JSR-231. (Look up information on the Java Community Process if you want more information on what this means.) This library lets you write code to control rendering in OpenGL. It is lightweight, unlike Java 3D, so it's easily embedded in existing swing windows in applications. Using this library, you're writing low level code, so it will take a lot more work than using Java 3D in order to get your scene rendering, but you do have a lot more control.


LWJGL

This is an alternative library to JOGL that provides most of the same features as JOGL, along with support for some other things like OpenAL(audio) and OpenCL(allowing use of GPU for CPU type tasks).

The advantages of JOGL and LWJGL (mainly the ability to embed in swing applications and the additional flexibility) make these the best option for me so far. The idea of using OpenGL programming is also exciting to me simply because I like the low level programming. However, I know it's not normally an efficient use of my time. Other people seem to agree, and have created a few libraries that use both JOGL and LWJGL as a base, but add scene-graph type abstraction to speed development.


Which brings me to...


jMonkeyEngine

This appears to be the main scene-graph library built on top of one of the low level libraries (LWJGL) that is in common use today. It has 9 years of development under its belt, and has even had a fork or two along the way. It seems to support most of the modern graphics abilities like bump mapping, shadows, parallax mapping and shader programming, etc, all in an easy-to-use quickly digestible form. For my purposes, this library may be a very solid choice.

Adding a jMonkeyEngine canvas to an existing Swing layout also seems to be as simple as adding a regular JComponent. Details are HERE if you're interested.

Special thanks to Erlend Sogge Heggen for corrections on the jMonkeyEngine information above.

----

I hope this serves as a good introduction to the 3D capabilities and libraries that you might use with Java. This is not a comprehensive list by any means, and I would encourage you to explore more on your own.


Now, a few fun links.

New jMonkeyEngine videos; Thanks Erlend
http://www.youtube.com/watch?v=WZ_alfF5Zt4
http://www.youtube.com/watch?v=8a0occCHl8Q

http://www.youtube.com/watch?v=5rxq_4brX_0 - Older jMonkeyEngine video, from JavaOne 2008

http://www.youtube.com/watch?v=8Dwyu3WKYDw - Ever seen bubblesort in 3d? JOGL

http://www.youtube.com/watch?v=BGnhpg2iaRo - Java 3d, simple house


Please feel free to leave comments if you have anything to add!

Wednesday, July 6, 2011

Adding Scripting Functions

So, you have a handy method available in one of your Java classes, and you wish you could use it in your jython script? Now you can!

The full list of items that are available from Python 2.5 are available here: http://docs.python.org/release/2.5/modindex.html

If we want to convert radians to degrees, let's say, there's no method in Python 2.5 to do it, but there is one in Java. And it couldn't be easier. java.lang.Math has a method called toDegrees(). Lets expose java.lang.Math straight to jython.

In your designer hook class, just add the following code:

 @Override
 public void initializeScriptManager(ScriptManager manager) {
  manager.addScriptModule("playground.math", java.lang.Math.class);
 }

Notice "java.lang.Math.class" in the last line. Giving the Class of an object will expose any static methods.

Now, open your designer, open up your script playground, and enter the following.

 import playground
 print playground.math.toRadians(.5)

And voila, we have full access!

If you want to expose any instance methods, just pass addScriptModule() an Object, and it takes care of the rest.

Of course, you can (and probably will) use your own classes and objects to create whatever scripting functions you need.

You'll want to run addScriptModule() in the client hook too, in order to have the same scripting functions available in both client and designer. If you need to run any of the scripting functions from a gateway script, be sure to add the lines to the gateway hook as well.

And that's it, easy as pie! (Maybe easier)

Note: As of Ignition 7.5.1, there's no way to "clean up" your scripting functions when your module is shut down. The Ignition developers have informed me that new methods (like manager.removeScriptModule(), for example) are planned to be added to Ignition in a future version.


Ignition Version: 7.5 (7.5.1)

Friday, June 10, 2011

Ignition Playground Project

As I've been writing Ignition Module code, I've found it useful to have a clean project to run some simple tests on so I can throw in code without affecting any of my live modules.

Here's my Playground project. Tested and running against Ignition 7.2, 7.3, 7.4, and 7.5 Module SDK.

I plan to keep this post updated as new versions are released.

Download it here: IgnitionModulePlaygroundv75.zip

Instructions:

1) Make sure you have the Module SDK.

2) Extract the files in the zip into your module workspace. (Default is "Module SDK 7.3.1\example". I copied that directory from the SDK to "C:\Development\Module SDK 7.3.1 Workspace\" on my system.)

3) Make sure the build-playground.xml and the module-playground.xml end up in the Build folder, alongside the Module SDK's build.xml.

4) In Eclipse, refresh your Build project, and import the three new projects. (Right click in the Package Explorer, go to "Import...", select General -> Existing Projects into Workspace, browse for the root directory, and click Finish.)

5) Note: From time to time, the requiredframeworkversion in the module.xml files need to be changed as new versions are released. Make sure your requiredframeworkverison matches that in the ModuleSDK's example module xml files. I try to keep the version in this download updated, so you may have to decrease the version number if you're using an old version of Ignition.

Enjoy!


Ignition Version: 7.2, 7.3, 7.4, 7.5

Thursday, May 26, 2011

Packaging an external library as a module

As I've been working with programming modules for Ignition, I've needed to use certain libraries along with my projects.

One way to do this: simply include the library's jars in your .modl file, and include them in your module.xml. This will make the library accessible for your module's classes' classloader.

What if the library is very large, though? This makes development take longer, since every module load requires reloading the entire library.

The solution I came up with was to package the library in its own module.

Steps
1) Grab the library's .jar files, and throw them in a directory.
2) Create a module.xml, like what I have below.
3) Package these in a .zip file, and change the extention to .modl

My library's module.xml:
<?xml version="1.0" encoding="UTF-8"?>
<modules>
 <module>
  <id>ApacheCXF</id>
  <name>Apache CXF</name>
  <description>The full Apache CXF 2.4.0 library and supporting libraries.</description>
  <version>2.4.0.1</version>
  <requiredignitionversion>7.2.5.76</requiredignitionversion>
  <requiredframeworkversion>2</requiredframeworkversion>
  <freemodule>true</freemodule>
  
  <!-- All the Apache CXF and CXF support libraries -->
  
  <jar scope="DG">antlr-2.7.7.jar</jar>
  <jar scope="DG">aopalliance-1.0.jar</jar>
  <jar scope="DG">asm-3.3.jar</jar>
                ...
 </module>
</modules>

Then, in your main module's module.xml, include this module as a dependency.

My main module.xml:
<?xml version="1.0" encoding="UTF-8"?>
<modules>
 <module>
  <id>myawesomemodule</id>
  <name>@NAME@</name>
  <description>@DESCRIPTION@</description>
  <version>@VERSION@</version>
  <requiredignitionversion>7.2.5.76</requiredignitionversion>
  <requiredframeworkversion>2</requiredframeworkversion>
  <freemodule>false</freemodule>

  <depends scope="G">ApacheCXF</depends>
  <depends scope="D">ApacheCXF</depends>

  ...

 </module>
</modules>

Note: A module can't have two dependencies. If your module already depends on fpmi, you'll need to move that dependency to the library module, and have your module just depend on the library module. So: "Your module" ->(depends)-> "Library module" ->(depends)->"fpmi"

Note 2: Your module will still load, even if it's missing dependencies. In your module's start up, make sure to check for the library's presence, and fault your module if it's not there.

Ignition Version: 7.2 (7.2.5)

Thursday, May 19, 2011

SQLTags DataType casting

One interesting feature when programming with SQLTags is that you can store just about any type of object you want within a tag.  Want a tag that stores a List?  Go for it!  Want to store a 3d model of your car?  Why not.

But hold on, maestro.  When Ignition gets a tag's value, it expects it in a certain form, so you actually can't just hand it your photo of Jennifer Aniston.

The Tag interface defines a few basic methods.  The ones we care about are:

public TagValue getValue()
public DataType getDataType()

TagValue (and its simple implementation BasicTagValue) contains this:

public Object getValue()

This method deceptively doesn't really want you to store an Object.  It's looking for that object to be a certain data type.  That data type is defined back up in your Tag object, not in your TagValue object.

In order to make sure your object conforms, a convenience method exists.

DataType.getTypeForClass(object.getClass());

If this method returns null, you know your object doesn't conform to any known data type.

If it does conform, why not create a new tag with that type?

BasicTagValue tagValue = BasicTagValue.fromObject(object);
BasicTag tag = new BasicTag(tagValue, DataType.getTypeForClass(object));

Note: make sure you use the correct DataType for sqltags:

com.inductiveautomation.ignition.common.sqltags.model.types.DataType

But wait! I actually *do* want to store a dancing baby in a tag, you say? Well, all you need to do is subclass BasicTagValue. Of course, you won't be able to hand Ignition's SQLTags browser the groovin picture, but you'll know it's there.

public class AnimationTagValue extends BasicTagValue {
 /** For serialization ** /
 @Deprecated
 public AnimationTagValue() {super();}

 public AnimationTagValue(AnimatedGif animation) {
  super(animation);
 }

 @Override
 public Object getValue() {
  AnimatedGif animation = (AnimatedGif) object;
  return animation.getNumberOfFrames();
 }

 public AnimatedGif getAnimation() {
  return (AnimatedGif) object;
 }
}

And while I suppose it would be possible to write a component for Vision that would display this data, it's really a bad idea. You don't want pictures of cats flying across your network and clogging up the tubes.  At least, not unless you've got mice in there.

Until next time.


Ignition Version: 7.2 (7.2.5)