Search code examples
javac++windowsopengllwjgl

Is it possible to mix C++ OpenGL code with Java using LWJGL?


I want to make Java app. using CEF3 library. CEF is the library to embedded Google Chrome browser in any app. And LWJGL is used to write GL code in Java. But before using CEF, basic question is how to mix C++ and Java.

  1. Java main calls C++ part as DLL

  2. C++ part creates window and set up GL context

  3. In the message loop, C++ call backs Java part again to do some GL work in Java.

Following test code fails with message:

FATAL ERROR in native method: Thread[main,5,main]: No context is current or a function that is not available in the current context was called. The JVM will abort execution.
at org.lwjgl.opengl.GL11.glColor3f(Native Method)
at Main.run(Main.java:18)
at Main.cppmain(Native Method)
at Main.main(Main.java:10)

Probably because Java part does not know about the GL context created by C++ part. My question is how can I set up GL context so that both C++ and Java may call GL functions?


Main.java

import org.lwjgl.opengl.GL11;

public class Main implements Runnable {
    {
        System.loadLibrary("cppgl");
    }

    public static void main(String[] args) {
        Main me = new Main();
        me.cppmain(me);
    }

    private native void cppmain(Runnable callback);

    @Override
    public void run() {
        // callback from cpp
        GL11.glColor3f(1.0f, 0.0f, 1.0f);
    }
}

cppgl.cpp

#pragma comment(lib, "OpenGL32.lib")

#include <windows.h>
#include <tchar.h>
#include <functional>
#include <gl/gl.h>
#include <jni.h>
#pragma comment(lib, "jvm.lib")

LRESULT CALLBACK WndProc(HWND hWnd, UINT mes, WPARAM wParam, LPARAM lParam) {
    if (mes == WM_DESTROY || mes == WM_CLOSE) { PostQuitMessage(0); return 0; }
    return DefWindowProc(hWnd, mes, wParam, lParam);
}

extern "C" JNIEXPORT void JNICALL Java_Main_cppmain
(JNIEnv *env, jobject, jobject callback) {

    LPCTSTR title = L"gl test";

    // prepare JNI call
    jclass cls = env->FindClass("Ljava/lang/Runnable;");
    jmethodID mid = env->GetMethodID(cls, "run", "()V");

    // window class
    HINSTANCE hInstance = ::GetModuleHandle(NULL);
    WNDCLASSEX wcex;
    ZeroMemory(&wcex, sizeof(wcex));
    wcex.cbSize = sizeof(wcex);
    wcex.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.hInstance = hInstance;
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszClassName = title;
    if (!RegisterClassEx(&wcex)) return;

    // window
    RECT R = { 0, 0, 640, 480 };
    AdjustWindowRect(&R, WS_OVERLAPPEDWINDOW, FALSE);
    HWND hWnd = CreateWindow(title, title, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, R.right - R.left, R.bottom - R.top,
        NULL, NULL, hInstance, NULL);
    if (!hWnd) return;

    // GL context
    PIXELFORMATDESCRIPTOR pfd;
    ZeroMemory(&pfd, sizeof(pfd));
    pfd.nSize = sizeof(pfd);
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = 32;
    pfd.cDepthBits = 24;
    pfd.iLayerType = PFD_MAIN_PLANE;
    HDC dc = GetDC(hWnd);
    int format = ChoosePixelFormat(dc, &pfd);
    SetPixelFormat(dc, format, &pfd);
    HGLRC glRC = wglCreateContext(dc);
    if (!wglMakeCurrent(dc, glRC)) return;

    // message loop
    ShowWindow(hWnd, SW_SHOW);
    MSG msg;
    do {
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else {
            wglMakeCurrent(dc, glRC);
            glClearColor(0.0f, 0.5f, 1.0f, 1.0f);

            // call Java and change clear color here
            env->CallVoidMethod(callback, mid);

            glClear(GL_COLOR_BUFFER_BIT);
            glRectf(-0.5f, -0.5f, 0.5f, 0.5f);
            glFlush();
            SwapBuffers(dc);
            wglMakeCurrent(NULL, NULL);
        }
    } while (msg.message != WM_QUIT);

    // clean up
    wglMakeCurrent(NULL, NULL);
    wglDeleteContext(glRC);
    ReleaseDC(hWnd, dc);
}


Solution

  • You sould call this once at begin before doing any rendering from java.

        // This line is critical for LWJGL's interoperation with GLFW's
        // OpenGL context, or any context that is managed externally.
        // LWJGL detects the context that is current in the current thread,
        // creates the GLCapabilities instance and makes the OpenGL
        // bindings available for use.
        GL.createCapabilities();