JNewton

Started by Gaara, February 25, 2006, 19:45:21

Previous topic - Next topic

Gaara

Hey all.  I've been using LWJGL for some time now and its really great.  I saw a post here that wanted a physics engine built-in and it got me thinking that I wanted one too.  So I made one.

I made a wrapper for the Newton Physics game engine so it can work with Java.  I am quite pleased with the results.  Right now it only does one or two basic callbacks.  I made this pretty much for myself and my projects, but if more people get interested in it, I will continue with a full port (if possible).

Here is a demo I made, which I got from one of the Newton demos.

Some quick notes:
-Requires Java 1.5, LWJGL is included in the zip.
-Right now it only works for the Windows Newton Physics dll, which is included in the zip.
-It uses an older version of Newton because the newer version's memory management is different and doesn't work well with Java
-I'm running a Athlon 64 3700+, 1Gig RAM, ATI 9800pro and there's very little slow down, another computer I've used it on is a Dell 3.4Ghz, 1Gig RAM, On-board video and there's also very little slow down.

Hopefully it works and you guys like it.

Fool Running

Programmers will, one day, rule the world... and the world won't notice until its too late.Just testing the marquee option ;D

Matthew Jones

We have the whole newton package wrapped. We have all the callbacks. Still have a few bugs and we are still in the proccess of trying to use it in game, but its wrote.

Matt

jjones7947

As Matt said above, we have the whole API wrapped. Used SWIG to wrap the normal functions and hand wrote the setup for the callbacks.  Used the JNI "onload" function to cache the JVM pointer, and all the callback data (methodID) and finally passed the pointers to the C callbacks to a Java class that stores them.  Have tried to use the newtonforeverybodyDo and everypolyDO to show the collision hull, but I think I'm not setting up the scene correctly.  Have taken time off to finish some house renovations but will get back to it.
Meanwhile, nice job and good luck with it.
Jim

Gaara

Ah cool.  I also wrapped the API, but have only implemented most of the methods, and one or two callbacks.  I guess I did it the hard way and re-created the .h file into a .java and then used javac to get the header from it.

I was going to ask how you guys do the callbacks.  I also used the 'onload' function to get the JVM pointer, but I do the callbacks differently.  I use interfaces for each callback and an internal table of bodies to callbacks so that the callbacks can be written in java.

Here is the source for TestGL.  Maybe you'll find out why your scene isn't working.

Thanks.

Matzon

OT: storing a pointer to the jvm onload - is that some sort of common knowledge? - coz when I did callbacks for fmod I was developing grey hairs about threads not having access the the VM until attached :evil:

jjones7947

Matzon,
It came in JNI_version_1_2, so may not have been available to save your hair color.  :)
The Sun JNI Book shows how under the section on "Load and UnLoad Handlers", I did it like this:
JavaVM *cached_jvm;
 jclass NPointers;
 
 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved)
 {
 	JNIEnv *env;
 	jclass cls;
 	cached_jvm = jvm;
 	
 	if((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_2))
 	{
 		return JNI_ERR;
 	}
 	printf("Cached the jvm\n");
 	cls = (*env)->FindClass(env, "newtonj/NewtonPointers");
 	if(cls == NULL)
 	{
 		return JNI_ERR;
 	}
 	printf("Cached the class\n");
 	NPointers = (*env)->NewWeakGlobalRef(env, cls);
 	if(NPointers == NULL)
 	{
 		return JNI_ERR;
 	}
 	printf("Set weak global reference\n");
 	return (JNI_VERSION_1_2);
 }

Then you need a function like this to get the current thread:
JNIEnv * JNU_GetEnv()
 {
 	JNIEnv *env;
 	(*cached_jvm)->GetEnv(cached_jvm, (void **)&env, JNI_VERSION_1_2);
 	return(env);
 }


Gaara,
Thanks for the source code, I will definitely see how your setting up the scene in Newton.
Anything I can do to help, let me know.  I'm using Eclipse but I can't debug the flow through both sides.  Even using VC7 doesn't seem to help.

Best,
Jim

Matzon

aye, I know how to do it an all that - it's more that a callbacks via a thread NOT created via Java cannot call VM commands unless it's attached - something that took me a very long time to find out - and I was wondering whether this was common knowledge

Gaara

Hey guys.  This is how I'm doing most of my callbacks.  Is this how your doing it or am I making it harder than it actually is?

//
// ForceAndTorque callback convertion
//
struct ForceAndTorqueCallback {
	jobject body;
	jobject callback;
	jclass c;
	jmethodID id;

	void setup() {
		c = GetEnv()->GetObjectClass(callback);
		id = GetEnv()->GetMethodID(c, "NewtonApplyForceAndTorque", "(LJNewton$NewtonBody;)V");
	}

	void Callback(const NewtonBody *b) {
		GetEnv()->CallVoidMethod(this->callback, this->id, this->body);//newNB(GetEnv(), body));
	}
};

//custom table collection
jHash<const NewtonBody*, ForceAndTorqueCallback*> _ForceAndTorqueCallback;
static void ForceAndTorqueCallbackWrapper(const NewtonBody *body) {
	ForceAndTorqueCallback *me = _ForceAndTorqueCallback[body];
	me->Callback(body);
}

JNIEXPORT void JNICALL Java_JNewton_NewtonBodySetForceAndTorqueCallback(JNIEnv *env, jclass, jobject body, jobject callback) {

	ForceAndTorqueCallback* cb = new ForceAndTorqueCallback();
	cb->body = env->NewWeakGlobalRef(body);
	cb->callback = env->NewWeakGlobalRef(callback);
	cb->setup();

	//converts java objects to structures
	NewtonBody *b = objToNB(env, body);
	_ForceAndTorqueCallback.Append(b, cb);

	NewtonBodySetForceAndTorqueCallback(b, ForceAndTorqueCallbackWrapper);
}

JNIEXPORT jobject JNICALL Java_JNewton_NewtonBodyGetForceAndTorqueCallback(JNIEnv *env, jclass, jobject body) {

	return _ForceAndTorqueCallback.Find(objToNB(env, body))->GetValue()->callback;
}


Thanks.

jjones7947

First, sorry I misunderstood your original question and inundated you with the code.  Now to the attaching question.  Do you mean that e.g. if the c code has a cached JVM (and by extension the current thread), methodID and a callback pointer, that in the c side callback function I need to attach my c thread before calling the Java function?
The JNIEnv is the current thread whether passed or derived (as above). That's why it can't be cached between trips across JNI, could see where if there was no way to get the current Java thread it would be necessary to attach, but maybe I'm still missing something.
Thanks
Jim

jjones7947

My flow goes like this:
Caching -> have a path through JNI which calls a c JNI function which looksup and caches the Java callback method. It then returns a jlong as a pointer to the c callback. This jlong is converted to a Java long which is stored in a SWIG pointer-to-function class in the PhysicsHandler Java class.

Running -> I call the native method which calls the function which takes the callback pointer as a parameter.  I'm passing the SWIG pointer. Ideally c has the pointer and when it needs to call, it calls the c function which gets the current thread, the methodID and any parameters and passes them back across the JNI wall.

My JNI functions are all in two classes. I don't use structs, but we're doing a lot of the same stuff.  For caching I've got 2 Java JNI methods and a c JNI function, on the call side I have the c side callback and 2 Java side methods. So a total of 6 method/functions. You have a struct and 3 functions on the c side so complexity looks about the same if your java mirrors your c.

One thing I'm noticing is that my c callback is not global which may account for it not being called. At work so can't try it, let you know later.
Jim