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

Open
dustContributor opened this Issue Nov 19, 2018 · 24 comments

Comments

Projects
None yet
5 participants
@dustContributor
Copy link

dustContributor commented Nov 19, 2018

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.

@ppiastucki

This comment has been minimized.

Copy link

ppiastucki commented Nov 19, 2018

Unrelated to LWJGL, but you can try ode4j if you do not mind pure Java solution.

@dustContributor

This comment has been minimized.

Copy link

dustContributor commented Nov 19, 2018

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 do mind.

@Ali-RS

This comment has been minimized.

Copy link

Ali-RS commented Nov 19, 2018

You can also take a look at jMonkeyengine bullet wrapper
https://wiki.jmonkeyengine.org/jme3/advanced/physics.html#technical-overview
https://github.com/jMonkeyEngine/jmonkeyengine

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
https://hub.jmonkeyengine.org/t/ecs-in-complex-3d-games-how-deep-should-it-go/40665/5?u=ali_rs
https://hub.jmonkeyengine.org/t/is-jmonkey-the-right-engine-for-simulating-basic-physics/40714/13?u=ali_rs

it will add bunch of dependencies though ;)

@dustContributor

This comment has been minimized.

Copy link

dustContributor commented Nov 19, 2018

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.

@dustContributor

This comment has been minimized.

Copy link

dustContributor commented Nov 22, 2018

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.

@Spasi

This comment has been minimized.

Copy link
Member

Spasi commented Nov 22, 2018

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 Newton.h with a single click.

@dustContributor

This comment has been minimized.

Copy link

dustContributor commented Nov 23, 2018

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 😄

@Spasi

This comment has been minimized.

Copy link
Member

Spasi commented Nov 26, 2018

On the Bullet bindings: I'm considering disabling Bullet for the 3.2.1 release until there's more feedback.

The bindings will still be available in snapshots after the release. This should give us enough time (until 3.2.2?) to evaluate other options before committing to anything. Maintenance effort should not be wasted on bindings that no one is going to use.

@dustContributor

This comment has been minimized.

Copy link

dustContributor commented Nov 27, 2018

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.

Spasi added a commit that referenced this issue Dec 8, 2018

@germangb

This comment has been minimized.

Copy link

germangb commented Dec 23, 2018

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

@dustContributor

This comment has been minimized.

Copy link

dustContributor commented Jan 4, 2019

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,
and after all the types were defined, it was missing the initial section that goes something like:

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.

@Spasi

This comment has been minimized.

Copy link
Member

Spasi commented Jan 4, 2019

Any updates on the template generator tool magic thingy?

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:

ant extract -Dclang=<path to libclang>

@dustContributor

This comment has been minimized.

Copy link

dustContributor commented Jan 6, 2019

Ah cool, then I already had it and didn't notice it 😄 Thanks!

@dustContributor

This comment has been minimized.

Copy link

dustContributor commented Jan 6, 2019

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:
https://github.com/LWJGL/lwjgl3/blob/master/modules/lwjgl/llvm/src/templates/kotlin/llvm/templates/ClangIndex.kt#L3059

long_long seems to be missing from the type definitions:
https://github.com/LWJGL/lwjgl3/blob/master/modules/generator/src/main/kotlin/org/lwjgl/generator/GlobalTypes.kt

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.

@dustContributor

This comment has been minimized.

Copy link

dustContributor commented Jan 6, 2019

I can launch the tool, but once I add Newton's folders and headers, I get this exception:

java.lang.NoClassDefFoundError: Could not initialize class org.lwjgl.llvm.ClangIndex$Functions
	at org.lwjgl.llvm.ClangIndex.clang_createIndex(ClangIndex.java:2706)
	at org.lwjgl.extract.ExtractionContext.<init>(Extract.kt:60)
	at org.lwjgl.extract.ExtractKt.parse(Extract.kt:174)

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.
EDIT2: Tried re-compiling and and running everything in both Java 8 and Java 11, still having the same issue.
EDIT3: I can't for the life of me make IDEA debug the ant process. I use the debug configuration "GENERATOR" but it wont stop at any breakpoint on the Kotlin code.

@Spasi

This comment has been minimized.

Copy link
Member

Spasi commented Jan 6, 2019

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:

ant extract -Dclang=<path to libclang> -Djvmargs="-Dorg.lwjgl.util.Debug=true -Dorg.lwjgl.util.DebugLoader=true"

Is there any useful output?

I use the debug configuration "GENERATOR" but it wont stop at any breakpoint on the Kotlin code.

Probably because this launches the Generator via Ant in a forked JVM process. Try setting fork to false (build.xml:271).

@dustContributor

This comment has been minimized.

Copy link

dustContributor commented Jan 6, 2019

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:

Probably because this launches the Generator via Ant in a forked JVM process

You're right, changed the fork to false and breakpoints started working.

Is there any useful output?

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:

ant extract

It complains that Dclang wasn't passed, if I pass it like:

ant extract -Dclang=path

It complains "-Dorg.lwjgl.llvm.clang.libname=path" wasn't passed. If I pass that like:

ant extract -Dclang=path -Dorg.lwjgl.llvm.clang.libname=path
// or
ant extract -Dclang=path -Djvmargs="-Dorg.lwjgl.llvm.clang.libname=path"

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

 <jvmarg value="-Dorg.lwjgl.llvm.clang.libname=path"/>

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.

@dustContributor

This comment has been minimized.

Copy link

dustContributor commented Jan 6, 2019

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:

A required function is missing: clang_Type_getObjCObjectBaseType

... 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?

ant extract -Dclang=/usr/lib/llvm-7/lib/libclang.so.1

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.

@Spasi

This comment has been minimized.

Copy link
Member

Spasi commented Jan 7, 2019

started generating like half of the JNI.java class

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.

Your tool hates me, Spasi.

Hmm, yeah, ant extract also uses fork="true" spawn="true". Fwiw, if I change a ClangIndex function name to something random, I can see the stacktrace in the tool's "console" panel. With spawn="false", I also get the same output on the terminal that runs ant extract.

Anyway, you can run the tool from within IntelliJ with the Template Extraction Tool run configuration. Just edit it to set the correct org.lwjgl.llvm.clang.libname. You should be able to debug normally like that.

@dustContributor

This comment has been minimized.

Copy link

dustContributor commented Jan 7, 2019

What version of clang are you using?

EDIT:

When some bindings are disabled, the set usually becomes smaller, so that's what's happening.

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?

Anyway, you can run the tool from within IntelliJ with the Template Extraction Tool

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:

  1. Launch it from IntelliJ pointing to libclang 8.
  2. Add dNewton, dgNewton and dgCore to the include paths.
  3. Add dgNewton/Newton.h as the main header.
  4. Click extract.

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:
https://gist.github.com/Spasi/0d064d5382ab2e037d04eca42aefa459#file-newton-kt-L1054
https://gist.github.com/Spasi/0d064d5382ab2e037d04eca42aefa459#file-newton-kt-L1062

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?

@Spasi

This comment has been minimized.

Copy link
Member

Spasi commented Jan 7, 2019

What version of clang are you using?

I'm using version 8, built from source (~Oct 27), on Windows.

if I point to Clang 8 I get a segfault

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 Newton.h. I did have some trouble parsing it, because the tool runs in C mode basically and the header had some invalid constructs (that are valid in C++). Fixed them manually and I also had to add --include=stdbool.h to the compiler arguments.

I was getting NPEs on the callbacks since those used structs that were defined later in the file.

Yes, this might happen, the tool outputs callbacks before structs. I can probably change this so that the header order is respected.

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?

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:

  • nullable to optional pointer parameters/members. When missing, LWJGL ensures (at runtime) that such values are never NULL.
  • Check to pointer parameters/members with known length (Check(1) is very common).
  • AutoSize/AutoSizeResult to integer parameters/members that specify the size of other, pointer parameters/members or return values. Several different cases here, you can find examples in other bindings.
  • char<Encoding> to null-terminated string parameters/members (e.g. plugInPath in NewtonLoadPlugins). This is not a modifier, you simply change the char type to a more specific one.
  • Unsafe to pointer parameters/members that cannot be made safe with any of the above (e.g. ptr in NewtonFree).
@dustContributor

This comment has been minimized.

Copy link

dustContributor commented Jan 11, 2019

Please attach a crash dump, it might be helpful.

hs_err_pid10248.log

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 version 8, built from source (~Oct 27), on Windows.

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

I have updated the gist above to the latest Newton.h
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.

Awesome, thanks!

Yes, this might happen, the tool outputs callbacks before structs. I can probably change this so that the header order is respected.

Yeah I think that'd be good.

Adding appropriate modifiers to pointer parameters is the most important part of creating LWJGL bindings [...] You must carefully examine the documentation/implementation of each function

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?

@dustContributor

This comment has been minimized.

Copy link

dustContributor commented Jan 12, 2019

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:
Going a bit further, it seems the tool also confuses multi dimensional arrays. There are a couple fields in Newton's C code that are like:

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:

 Can't get https://build.lwjgl.org/nightly/linux/x64/newton/libdVehicle.a to /home/user/git/lwjgl3/bin/libs/linux/x64/newton/libdVehicle.a

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?

@Spasi

This comment has been minimized.

Copy link
Member

Spasi commented Jan 12, 2019

Which generates a callback with a void argument. Legal in C, not so legal in Java.

Not related to Java. The template DSL is supposed to look like C and it actually has some of its quirks (e.g. using (void) on functions with no arguments). This must be the first time it's occurred on a callback though. It's a minor bug and will be fixed soon. For now, simply drop void().

it seems the tool also confuses multi dimensional arrays

Thanks, will have a look.

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?

Correct. Unfortunately, Bullet doesn't support shared library builds, so LWJGL links it statically. We should use Newton as a shared library if possible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment