-
-
Notifications
You must be signed in to change notification settings - Fork 634
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bullet C API first thoughts #428
Comments
Unrelated to LWJGL, but you can try ode4j if you do not mind pure Java solution. |
I do mind. |
You can also take a look at jMonkeyengine bullet wrapper And if you want a sever side bullet physic simulation with an EntityComponentSystem design like what I am doing for my mmorpg, you can consider it will add bunch of dependencies though ;) |
Yeah, trading libGDX for jMonkeyInterop is a side step for me, maybe a step back since jme is even bigger. In any case, I think this is an issue better suited for a native lib rather than a Java solution, the question is which one. I didn't know there were other physics engines with a C API, for some reason I thought the other physics engines were C++ only like Bullet was. So I suggested Bullet's C API to Spasi as soon as I found it existed out of ignorance mostly, didn't know about Newton Engine's . My line of thinking was "Hey, I used Bullet before and it was really straightforward and easy to integrate, its C API cant be that much different!". I was wrong. |
Well I cloned Newton's repo and LWJGL's repo since this is a good moment as any to try learn how to make a new binding with it (not that I have the expectation of getting something usable out of my attempt). From what I've read in https://github.com/LWJGL/lwjgl3/tree/master/doc what I ought to do, since I've followed the setup steps and everything seems to work, is just grab this header file, create a newton.kt and NewtonTypes.kt file, grab a cup of coffee and start typing the data type definitions, and the function definitions? For the function definitions a chunk of the work could be done with some clever find+replace, but for the data type definitions it seems it's just a lot of typing. |
You may want to wait a few days before attempting to create new bindings. I've been working on a tool that extracts LWJGL template definitions from C/C++ headers. I'm currently in the process of trying different headers to fix corner cases and polish it up. The tool uses libclang (via new Clang/LLVM bindings) to parse the headers and traverse the AST. It detects both declarations and associated comments for documentation. Comments in doxygen format are supported and can be traversed to detect e.g. param/return documentation, but the tool tries to attach any comments it can find. This is what it currently generates for |
That is awesome, dude! Really, really cool. Sadly now you solved the part I thought I understood better, and now I have to actually try to compile the thing 😄 |
On the Bullet bindings: I'm considering disabling Bullet for the The bindings will still be available in snapshots after the release. This should give us enough time (until |
I agree. I do think that LWJGL should offer a physics engine binding, since at least for game development it'd leave LWJGL more rounded. I just don't think Bullet's C API is the one. |
I've been using Newton lately and the C API is indeed very clean and nice to work with 💯 . It would be a great addition to lwjgl |
Any updates on the template generator tool magic thingy? I gave a quick jab at making that gist working. It's missing a couple things. For example, the definition of the type 'long_long' (which I believe it'd just map like an int64_t), also _Bool, val Newton = "Nuklear".nativeClass(Module.NEWTON, prefix = "", prefixMethod = "", library = "lwjgl_newton") {
// yadda yadda
} And I'm getting an NPE while ant generate'ing NewtonUserMeshCollisionRayHitCallback that I don't understand because I have zero clue about Kotlin 😄 Probably there is more stuff I haven't reached yet. |
Yes, it's been available for a while. It's fully featured now, but I'm sure there are still corner cases that are not handled properly, so please consider it a beta and report any issues you face. You'll need to download or build LLVM/Clang on your machine, then run the tool with:
|
Ah cool, then I already had it and didn't notice it 😄 Thanks! |
To try out the tool, it seems I have to compile first LLVM bindings. So I try to do that with just enabling LLVM in build-bindings.xml, then doing 'ant compile-templates' but I'm having issues. First there is this: long_long seems to be missing from the type definitions: I add it like: val long_long = IntegerType("long long", PrimitiveMapping.LONG) And if I try to compile again, I get an "overload resolution ambiguity" error on those long_long returning functions. EDIT: Nevermind, I mixed up with the old 'long_long' I had added for Newton, turns out LLVM's bindings had its own long_long defined which clashed with the global one I added. |
I can launch the tool, but once I add Newton's folders and headers, I get this exception:
It's like ClangIndex is there, but the inner Functions class isn't. EDIT: Yeah I really don't get it, inspecting the generated ClangIndex.class and ClangIndex$Functions.class, they're there. No idea why the tool can't find it. |
This usually means that static initialization of the inner Functions class failed. For example, it can happen if you're using an older libclang version that doesn't expose a newer function pointer. I haven't done any work on versioning the LLVM/Clang bindings, it's likely that the latest version is required. Try running the tool with:
Is there any useful output?
Probably because this launches the Generator via Ant in a forked JVM process. Try setting |
I'm not entirely sure why but I fired up IntelliJ today and it started generating like half of the JNI.java class, a lot of "invokeXYZ" functions were missing. I just reverted it to the repo's version since that had everything but I have no clue what went wrong. In any case:
You're right, changed the fork to false and breakpoints started working.
It launches normally but there isn't any additional output. I mean, it launches in a separate process like you said at first, so there is no debug output (or any output) from the terminal where I launch it. If I prevent the 'extract' tool to be forked with the flags, I can no longer pass the required arguments to the process (like clang's path). They seem to be getting ignored. I mean, if I launch with the debugger from IntelliJ like:
It complains that Dclang wasn't passed, if I pass it like:
It complains "-Dorg.lwjgl.llvm.clang.libname=path" wasn't passed. If I pass that like:
It also gets ignored, it doesn't seems to reach the Configuration class. I also tried setting it directly from the build.xml file like
But it's still null in the program when I put a breakpoint in main. I'm guessing the debug flags are getting ignored too. |
All right. Managed to get the debugger working via adding a bunch of flags to the extract tool in build.xml (Xdebug, debugging agent and friends) then attaching IntelliJ's debugger after the ant task forked the process and adding an exception breakpoint. With that I managed to inspect the inner exceptions on a "ExceptionInitializerError" that occurred the first time (after that the JVM just throws the NoClassDefFoundError exception if you try to extract the definitions again). Turns out the message is:
... But Google knows nothing about "getObjCObjectBaseType" In any case, my libclang version is 7.0.1-1, from Debian's "testing" repositories, is that recent enough? Or am I pointing to the wrong .so?
EDIT: Turns out 'experimental' repositories have libclang 8, so I used that instead and now ClangIndex gets initialized correctly, but I am getting an UnsatisfiedLinkError on JNI.invokeP when ClangIndex.clang_createIndex gets called. I'm not understanding if it means there is some clang stuff missing, or if the native core of LWJGL isn't getting loaded. EDIT2: Well turns out that even the native part of the JNI.java class got halfway generated so it was also missing a bunch of invokeXYZ functions, that's why invokeP threw an unsatisfied link error. So I recompiled that, with the complete .c file, and now my grand error message is:
Your tool hates me, Spasi. |
The LWJGL generator gathers all native calls necessary to support the various bindings and creates a deduplicated set (based on call convention and signature) that goes into the JNI.java class. This makes the core LWJGL shared library smaller. When some bindings are disabled, the set usually becomes smaller, so that's what's happening.
Hmm, yeah, Anyway, you can run the tool from within IntelliJ with the |
What version of clang are you using? EDIT:
So I only had LLVM/Clang bindings enabled, but the generated JNI.java class (and its native counterpart) didn't have invokeP and others in it, which is used by the LLVM/Clang bindings. Is that ok?
Geezus you're right, why didn't I see it before. That launches the tool fine, if I point to Clang 7 I get the initialization error, if I point to Clang 8 I get a segfault so the debugger ain't much help there. First segfault was in some deep Swing code, second was in a native stack frame I think. So it looks like a memory corruption thingy. Kinda weird. What I do is:
Am I doing something wrong? EDIT2: Even if I cant use the generator, I can try with the gist you posted earlier. So far I'm stuck on the generate part. First thing I noticed was that the order of declaration was important in the type definitions. I was getting NPEs on the callbacks since those used structs that were defined later in the file. Once I got that sorted out I started to get "Data pointer not validated" exceptions on a few of the functions like: What does it means? I can just add "Unsafe.." to them but I'm not sure what is it doing, what kind of validation is it expecting? Like a length/size parameter? |
I'm using version 8, built from source (~Oct 27), on Windows.
Please attach a crash dump, it might be helpful. When I have time, I'll do some testing on Linux. I'll also try to make 8.0 functions optional, so that the tool can run on a stable 7 build. For now, I have updated the gist above to the latest
Yes, this might happen, the tool outputs callbacks before structs. I can probably change this so that the header order is respected.
Adding appropriate modifiers to pointer parameters is the most important part of creating LWJGL bindings and it's something that cannot be automated. You must carefully examine the documentation/implementation of each function and, at the very least, add the following modifiers:
|
For some reason I get a segfault if I launch it from IDEA. If I launch it from a terminal, then attach the debugger, I get garbled console output in the tool. This is testing with libllvm 8.
I'm using libllvm 8 from revision 350193 in their SVN repo, which is ~700 commits behind current master (350916), it's what Debian 'experimental' repos have. The 'testing' repos have libllvm 7.0.1
Awesome, thanks!
Yeah I think that'd be good.
Ah so you're saying I should know what I am doing, any more unreasonable requests? 😄 I have one question, when you do the 'nativeClass' extension method, the "prefixMethod" is the string that every method will be prefixed with ("Newton" in this case), but what is the plain "prefix"? Is it for constants? |
I found this thing with the generator, look at this callback: typedef dLong (*NewtonGetTimeInMicrosencondsCallback) (); Seems fine, no args and returns a long. Now the generated template for this is: val NewtonGetTimeInMicrosencondsCallback = Module.NEWTON.callback {
long_long(
"NewtonGetTimeInMicrosencondsCallback",
"",
void()
) {
documentation = "Instances of this interface may be passed to the #FIXME() method."
}
} Which generates a callback with a void argument. Legal in C, not so legal in Java. This has the problem that when you ask it to generate the .java files, it does this: @Override
public long invoke(void *) {
return delegate.invoke(*);
} Which is invalid. The C code seems fine so I'm guessing this is something the template generator has to transform? Or the template processor should not add an argument if it's void? EDIT: dFloat m_offsetMatrix[4][4] With the tool seemingly generating: float.p("m_offsetMatrix", "")[4] Which causes issues later on on .java code generation, since it tries to pass a FloatBuffer to a function that doesn't accepts one when generating the accessor for that field. I fixed it with float("m_offsetMatrix", "")[16] EDIT2: Well I wanted to try out just generating bindings for one of Newton's C examples, so far I got the types and I'd need to generate bindings for a couple functions only. I got NewtonTypes.kt working, so I wanted to try out the native compilation step before making the few function bindings in Newton.kt. I'm not understanding the build process, is it trying to download compiled artifacts from your build server? I'm getting exceptions like:
EDIT4: I think I understand, I copied the build section from Bullet, and that one has a bunch of sections. <!-- NEWTON -->
<build module="newton" simple="true" linker="g++" if:true="${binding.newton}">
<beforeLink>
<mkdir dir="${lib}/${platform}/${build.arch}/newton"/>
<parallel threadsPerProcessor="2" failonany="true" unless:set="lib-uptodate">
<update-dependency name="Newton01" artifact="${build.arch}/newton/libdMath.a"/>
<update-dependency name="Newton02" artifact="${build.arch}/newton/libdgCore.a"/>
<update-dependency name="Newton03" artifact="${build.arch}/newton/libdScene.a"/>
<update-dependency name="Newton04" artifact="${build.arch}/newton/libnewton.a"/>
<update-dependency name="Newton05" artifact="${build.arch}/newton/libdNewton.a"/>
<update-dependency name="Newton06" artifact="${build.arch}/newton/libdVehicle.a"/>
<update-dependency name="Newton07" artifact="${build.arch}/newton/libdgPhysics.a"/>
<update-dependency name="Newton08" artifact="${build.arch}/newton/libdAnimation.a"/>
<update-dependency name="Newton09" artifact="${build.arch}/newton/libdContainers.a"/>
<update-dependency name="Newton10" artifact="${build.arch}/newton/libdCustomJoints.a"/>
<update-dependency name="Newton11" artifact="${build.arch}/newton/libtinyxml.a"/>
</parallel>
</beforeLink>
<link>
<arg value="-ldl"/>
<arg value="-L${lib}/${platform}/${build.arch}/newton"/>
<arg value="-lNewton01"/>
<arg value="-lNewton02"/>
<arg value="-lNewton03"/>
<arg value="-lNewton04"/>
<arg value="-lNewton05"/>
<arg value="-lNewton06"/>
<arg value="-lNewton07"/>
<arg value="-lNewton08"/>
<arg value="-lNewton09"/>
<arg value="-lNewton10"/>
<arg value="-lNewton11"/>
</link>
</build> I'm guessing those kind of libraries are the ones you don't commit to the repo because they're too big, so you make it download the artifacts from a build server, right? |
Not related to Java. The template DSL is supposed to look like C and it actually has some of its quirks (e.g. using
Thanks, will have a look.
Correct. Unfortunately, Bullet doesn't support shared library builds, so LWJGL links it statically. We should use Newton as a shared library if possible. |
Literally uncharted lands 😄
Yeah it has a shared library flag and everything. But now I'm wondering, it looks like it wont be possible to build it with just the <build_simple> script, it has a bunch of CMakeLists.txt inside defining each a separate module, this means I'd have to pull all .so files from somewhere in my local file system for now. How do I do that? Also I'm not understanding if LWJGL supports loading multiple .so/.dll for a single library, or if it expects everything to be packaged into a single dependency. Which one is it? EDIT: I just found out that you can make a single .so out of multiple .a with GCC, by simply sitting on the output folder and doing:
I had read it before but I didn't understood what it was until Newton guy pointed it out to me. I'm not entirely sure if there is any drawback in using it that way instead of going and editing the ~10 CMakeLists.txt in Newton so it outputs a single .so, maybe some optimizations are lost in the way since it works with pre-compiled code? No idea honestly. EDIT2: Well I think I managed to compile something, I mean, a 1.2MB liblwjgl_newton.so file at least. I ended up with this build config: <!-- NEWTON -->
<build module="newton" simple="true" linker="g++" if:true="${binding.newton}">
<beforeLink>
<mkdir dir="${lib}/${platform}/${build.arch}/newton"/>
<copy todir="${lib}/${platform}/${build.arch}/newton" flatten="true">
<path>
<pathelement path="${src.main}/src/build/lib/libdMath.a"/>
<pathelement path="${src.main}/src/build/lib/libdgCore.a"/>
<pathelement path="${src.main}/src/build/lib/libdScene.a"/>
<pathelement path="${src.main}/src/build/lib/libnewton.a"/>
<pathelement path="${src.main}/src/build/lib/libdNewton.a"/>
<pathelement path="${src.main}/src/build/lib/libdVehicle.a"/>
<pathelement path="${src.main}/src/build/lib/libdgPhysics.a"/>
<pathelement path="${src.main}/src/build/lib/libdAnimation.a"/>
<pathelement path="${src.main}/src/build/lib/libdContainers.a"/>
<pathelement path="${src.main}/src/build/lib/libdCustomJoints.a"/>
<pathelement path="${src.main}/src/build/lib/libtinyxml.a"/>
</path>
</copy>
</beforeLink>
<link>
<arg value="-ldl"/>
<arg value="-L${lib}/${platform}/${build.arch}/newton"/>
<arg value="-ldMath"/>
<arg value="-ldgCore"/>
<arg value="-ldScene"/>
<arg value="-lnewton"/>
<arg value="-ldNewton"/>
<arg value="-ldVehicle"/>
<arg value="-ldgPhysics"/>
<arg value="-ldAnimation"/>
<arg value="-ldContainers"/>
<arg value="-ldCustomJoints"/>
<arg value="-ltinyxml"/>
</link>
</build> That is, cmake && make'ing the library manually, then letting the script copy back the resulting .a, then linking them up all together into a single .so. I noticed I wasn't getting any generated C file and I didn't realize that the libraries have a "nativeDirective" call in the template to include the required header files. So I copied Newton.h into the main/c folder, fixed two warnings, and compiled all the types plus just two random functions (WorldGetVersion and something else) to try it out. Now, given that I have a lwjgl_newton.so, and a bunch of .class files, how do I package all of this up? |
If you export the
Multiple shared libraries for a single binding should be fine. Currently the LLVM/Clang bindings work like that. The CUDA bindings will also use multiple shared libraries soon, when more CUDA Toolkit libraries are supported. In case multiple shared libraries need to be bundled with LWJGL, the
This is the preferred solution. Should be done for all platforms in CI, then LWJGL simply downloads and bundles a single shared library (like bgfx, GLFW, OpenAL, etc.).
This is different from using a shared library and calling it via Just to be clear:
The first solution is very simple to configure in LWJGL, but you'll need to worry about bundling the static libraries as a shared library in the CI scripts. Doing this for all 3 platforms and making sure the functions are properly exported (see |
So I found the release target while searching for binding.bullet but it fails with:
The only binding I (think) have enabled is Newton. I dont understand why it says "nuklear: FAILED", it isn't even compiled. I have no clue why it says the "package is empty or does not exist" either. The Newton .class files are generated and with that exact package... Although I don't see a module-info.class in the Newton folder inside bin now that I think of it. I added it in newton/src/resources/module-java.info though. I was launching the ant generate/compile/etc tasks with Java 8 as JAVA_HOME, should I have used Java 11? |
Callbacks are evaluated in a final step after the generation of all bindings has succeeded. Most likely, you were hitting errors earlier in the process and never got to generating callbacks before. So, I'll be waiting your evaluation on how the Newton bindings feel. My biggest worry is that there are too many callback types and upcalls are super-slow via JNI (at least an order of magnitude slower than downcalls). I'm not familiar with Newton's API, but if the number of callback invocations depends on scene complexity, JNI overhead will probably limit scalability. |
Yeah, now that you mention it, you're probably right.
It's true, but I don't think it can be helped. AFAIK most physics engines do most of their contact/collision/intersection events through callbacks, I don't think Newton is "special" in that regard. |
So would it be possible to include Newton in the latest nightly or provide other means of download? |
I got new developments 😄 https://gfycat.com/happygoluckyaltruisticanemonecrab This is using the few implemented Newton functions in my LWJGL fork. Apparently the "apply force and torque" callback is called for all bodies every "tick" in Newton, unless the body is static (ie, mass 0). Kinda curious too that you have to apply gravity in said callback "manually", instead of having a "NewtonSetWorldGravity(long handle, float gravity)" like you'd have in other libraries. I guess it could impact performance like Spasi said. Anyway, next milestone is implementing a 'kinematic' body for the "player", that is, a body that is moved by forces outside the simulation that still can collide and stuff.
You could try to clone my LWJGL fork I guess... Then try to build it. This isn't something Spasi is upstreaming, rather it's just stuff I am doing on my own LWJGL fork so Spasi can keep working on The Great Plan undisturbed by my incessant questions. |
Hey @Spasi I have some issues, I am trying to map this function: void* NewtonWorldAddListener (const NewtonWorld* const newtonWorld, const char* const nameId, void* const listenerUserData) Afaik the The In any case, even if I add those, it complains on the sizing of the returned pointer:
Gives:
That's an opaque pointer I think, and I have no clue how to tell LWJGL not to worry about it. I CTRL+F'd around for a bit and I haven't found templates that does this. |
NanoVG's val NVGcontext = "NVGcontext".opaque
...
val ctx = NVGcontext.p |
That's a type definition, not a function definition. I am trying to define a function, as you can see in the code I wrote. EDIT: Unless I'm missing something I've been looking over 15 bindings and nothing has an un-sized void* as a return value 🤔 |
This case is similar, though I guess not enough perhaps. This makes a proper opaque return for the The corresponding C for that function is: I would think you can do something similar with |
Well using "void".opaque works on the return type, it generates this on the native side: JNIEXPORT jlong JNICALL JavaCritical_org_lwjgl_newton_Newton_nNewtonWorldAddListener(jlong newtonWorldAddress, jlong nameIdAddress, jlong listenerUserDataAddress) {
NewtonWorld const * const newtonWorld = (NewtonWorld const * const)(intptr_t)newtonWorldAddress;
char const * const nameId = (char const * const)(intptr_t)nameIdAddress;
void * const listenerUserData = (void * const)(intptr_t)listenerUserDataAddress;
return (jlong)(intptr_t)NewtonWorldAddListener(newtonWorld, nameId, listenerUserData);
} I guess that would work, both on 32 and 64 bit, but I'm not sure if it's how it should be used. In any case, that's enough for trying stuff out stuff, thanks! |
That looks correct, given the output of the |
Cool! I also used it for the functions that expected the void* listener as a parameter, since LWJGL would generate a ByteBuffer parameter with Unsafe..void.p which doesn't really makes sense in this context. |
Hey @dustContributor,
No, use
There's
|
Ah right, I think I've seen it before but I forgot about it.
Makes sense, thanks! |
I hit an issue when adding a bunch of functions: Error:
Function definition in Newton.h NEWTON_API dFloat NewtonHingeCalculateStopAlpha (const NewtonJoint* const hinge, const NewtonHingeSliderUpdateDesc* const desc, dFloat angle); Binding in templates/Newton.kt float(
"HingeCalculateStopAlpha",
"",
NewtonJoint.const.p.const("hinge", ""),
NewtonHingeSliderUpdateDesc.const.p.const("desc", ""),
float("angle", "")
) Generated native glue in org_lwjgl_newton_Newton.c JNIEXPORT jfloat JNICALL JavaCritical_org_lwjgl_newton_Newton_nNewtonHingeCalculateStopAlpha(jlong hingeAddress, jlong descAddress, jfloat angle) {
NewtonJoint const * const hinge = (NewtonJoint const * const)(intptr_t)hingeAddress;
NewtonHingeSliderUpdateDesc const * const desc = (NewtonHingeSliderUpdateDesc const * const)(intptr_t)descAddress;
return (jfloat)NewtonHingeCalculateStopAlpha(hinge, desc, angle);
}
JNIEXPORT jfloat JNICALL Java_org_lwjgl_newton_Newton_nNewtonHingeCalculateStopAlpha(JNIEnv *__env, jclass clazz, jlong hingeAddress, jlong descAddress, jfloat angle) {
UNUSED_PARAMS(__env, clazz)
return JavaCritical_org_lwjgl_newton_Newton_nNewtonHingeCalculateStopAlpha(hingeAddress, descAddress, angle);
} I don't understand, the function looks ok, the parameters look ok, the generated glue looks ok. I have the same issue with 3-4 more functions. On a side note, after using it for a bit I've realized that a whole bunch of Newton code isn't and probably wont be exposed to Java. There is the core API, afaik defined in dgNewton, dgPhysics, dgCore, and probably dMath and dNewton although I'm not sure. This is exposed as C functions through Newton.h, but there are a lot of side modules (dVehicle, dAnimation, dCustomJoints, dScene, etc) that look more like convenience C++ classes that use the base C API to do a bunch of things. This means I don't think we'd really need most of the dXYZ libraries linked into the main .so for the base C API to work, this is probably what the CMake 'NEWTON_BUILD_CORE_ONLY' flag does. |
Looks like there's no implementation for this function in |
Duh it didn't occur to me to look at the .cpp, thanks, I'll comment them out for now then. |
Spasi, I have a question about callbacks. I have a call like this: var contacts = NewtonWorldConvexCast(newtonWorld,
mat4Buffer,
vec3Buffer,
collider,
float1Buffer,
MemoryUtil.NULL,
(body, shape, u) -> owner.body == body ? 0 : 1, // Capturing lambda
contactBuffer,
0); The capturing lambda is a callback with the signature (long, long, long) -> int. It's a filter for the cast call, so the filter is run for each body colliding with the convex shape, and returns if it should be included in the resulting contact list or not. If I do that inside a loop, LWJGL will allocate in native memory a new callback every time right? I mean, those have to be freed manually? Is there a way to stack allocate them? Or this isn't the case? |
Yes.
Yes. If the debug allocator is enabled, it will tell you if you're leaking callbacks.
No. Callbacks must live in executable memory. The best approach is to instantiate (and reuse) a callback instance instead of a lambda. Note that each callback type is mapped to a You can use the |
I see, thanks! Do ThreadLocals work if the callbacks are called from threads created on the native side? That is, do they get run under a JVM thread regardless when transitioning back to Java-land? Another thing I am wondering is that unless I am misunderstanding something, there isn't a way to reuse callback instances without giving up the "type safe" call. For instance, if I have a signature like Whatever I pass as SomeCallbackI instance, it calls SomeCallbackI#address inside that LibMethod function, then delegates to nLibMethod. That 'address' call will in turn call Callback#create, which will allocate a new executable callback right? So if I want to have a reusable callback, I have to do it like: // In a constructor/init method/somewhere
this.callbackAddress = ((SomeCallbackI)(a) -> doStuff(a)).address();
// then when calling the native method in a loop
Lib.nLibMethod(memAddress(fba), memAddress(fbb), this.callbackAddress); So I got my reusable callback, but I had to give up the automatic null/size checks for those float buffers since I am using the 'n' version of the method. Is this is how its done? Or am I missing something? |
Foreign threads that attach (dynamically and asynchronously) to the JVM are mapped to a I was talking about calling a function from Java that does some work and triggers callbacks back into Java. Those callbacks will be invoked on the same thread that initiated the action (a good example is
There is a way, I have explained how in my previous reply:
Using your example: SomeCallback cb = SomeCallback.create(a -> doStuff(a)); // reusable
Lib.LibMethod(fba, fbb, cb);
|
Ahh right, I didn't see that Callback#address override that just returns the long field, makes sense, thanks! |
There's another bullet C wrapper here: https://github.com/AndresTraks/BulletSharpPInvoke/tree/master/libbulletc that is a lot closer to the C++ API. I don't know if it is better than Newton or not. |
We now have another version of the Bullet bindings (libbulletc #480). The API is a straightforward wrapper around the C++ API, which is as bad as it sounds, but still better than the "official" client/server C API. It should be available for testing in the first snapshots after the 3.2.3 release. Interestingly, like Newton, the C++ API has too many callbacks. Newton has 54 callback types and it looks like Bullet has more than that. Also, you end up calling tons of trivial C++ methods (e.g. JNI overhead is going to hurt. So, yeah, it doesn't look good on the physics front. We'll probably have bindings for both Bullet and Newton soon, to gather user feedback, but I'm still open to better suggestions. |
To anyone still looking forward to this, I'm sorry to say that I've removed the Bullet Physics bindings from LWJGL. I was not happy with it technically (see discussion above) and there is simply not enough maintenance "budget" available to be dealing with such an awkward library. Alternative solutions:
|
Hi! This is more of an opinion but now that I've been trying to port my stuff to Bullet's C API and worked a bit with it I'm having doubts about it.
The API itself is very awkward to use. I didn't think it'd be a problem but all the indirect handles and indices you have to keep around to operate on things are very annoying.
The API refuses to work with structs directly, since it's thought as a client-server architecture, what you have on the "client side" are usually int64 handles or int32 indices referring to some item on the server side, and a set of many functions that each mutate/read a field of the logical structure on the server side, one by one.
The API is robotics and Python oriented, so I am guessing it only exposes what is useful and practical for both of them.
This means that something really useful in games like collision filters isn't there by default, you have to enable it through a plugin. Moreover, something like a kinematic body (like say, a player) isn't there at all, so instead of setting the velocity of the player you have to move it around with instantaneous forces (unless there is actually a way and I'm an idiot), using forces this way is very hard to fine tune player movement or anything really.
This also means there is a lot of fluff there that isn't relevant for games at all. There are a lot of core functions there purely dedicated to work with the included visualizer of the physics simulation. Until very recently the only way to load an arbitrary mesh body was through a robotics focused file format, instead of you know, an array of vertices.
What I am trying to say is that the client-server layer between the user and Bullet isn't helping at all. It's just bloat a game doesn't needs.
Now the thing is that for 3D there aren't many physics libraries to use out there. And out of them all, Bullet is probably the most documented and widely used, although that's the case for the plain C++ library, not the C API, I doubt there is any game made with it.
It doesn't means there aren't any alternatives:
Newton Dynamics has a C API, seems pretty clean and it has been used on a few relevant games(Mount & Blade, Frictional Games stuff, ie SOMA and Amnesia). Even less examples although it has a demo project with a bunch of things. It's zlib licenced.
Open Dynamics Engine has been used on a bunch of games (Techland stuff, ie Call of Juarez, Dead Island) and I've read it has a C API... I just don't understand enough of the repo to know where to look for it. Looking for examples I get results from 2006. It's BSD or LGPL licenced. There is a Java port of it but it uses 64 bit floats everywhere and the first issue you see in the repo is an unresolved high garbage creation complaint from 2015, both of them bad signs.
I think I like more Newton Dynamics since it looks more like what you'd expect of a "proper" C library. Like a single header with most of the structs and function definitions for starters. I don't really know how hard it'd be to have bindings for it too so people (like me!) can try it out.
In any case, for now I'll keep trying to work with Bullet's C API and probably nag the author about some missing functionality as/if I find it, since as I've said in another occasion, with it I can get rid of libGDX as a dependency in my project which is a big plus for me.
The text was updated successfully, but these errors were encountered: