A squash of hundreds of commits with a binding API generator. The API generator works with a hybrid model: 1. We define methods and fields in the code. 2. Mark them with API annotations. 3. For specific use cases, we use the old approach with JNI generation comments. There is also an API to generate an AST tree, which is saved to JSON. This JSON can be used for full API generation by declaring only field/method names. Currently, it is used to generate constants. By eyeballing, 95% of the API has been converted to the new approach. This is sufficient for the current moment. Only edge cases with specific API calls are left. fix: ambiguous call for ImGui::GetColorU32 build: disable checkstyle for imgui-binding module Since the module is heavily generated, checkstyle for it doesn't make sense. build: make buildSrc to use JDK 11 build: make generateApi optional when compiling project Initially generated module was not included to the project VCS. Since it's now in the separate src folder, we don't need to call generator every time. Only when a new generation result is required.
341 lines
12 KiB
Java
341 lines
12 KiB
Java
import imgui.ImGui;
|
|
import imgui.ImVec2;
|
|
import imgui.extension.imguizmo.ImGuizmo;
|
|
import imgui.extension.imguizmo.flag.Mode;
|
|
import imgui.extension.imguizmo.flag.Operation;
|
|
import imgui.flag.ImGuiCond;
|
|
import imgui.flag.ImGuiInputTextFlags;
|
|
import imgui.flag.ImGuiWindowFlags;
|
|
import imgui.type.ImBoolean;
|
|
import imgui.type.ImFloat;
|
|
|
|
import java.awt.Desktop;
|
|
import java.net.URI;
|
|
import java.util.Arrays;
|
|
|
|
import static org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT_SHIFT;
|
|
import static org.lwjgl.glfw.GLFW.GLFW_KEY_R;
|
|
import static org.lwjgl.glfw.GLFW.GLFW_KEY_S;
|
|
import static org.lwjgl.glfw.GLFW.GLFW_KEY_T;
|
|
|
|
public class ExampleImGuizmo {
|
|
private static final String URL = "https://github.com/CedricGuillemet/ImGuizmo/tree/f7bbbe";
|
|
|
|
private static final int CAM_DISTANCE = 8;
|
|
private static final float CAM_Y_ANGLE = 165.f / 180.f * (float) Math.PI;
|
|
private static final float CAM_X_ANGLE = 32.f / 180.f * (float) Math.PI;
|
|
private static final float FLT_EPSILON = 1.19209290E-07f;
|
|
|
|
private static final float[][] OBJECT_MATRICES = {
|
|
{
|
|
1.f, 0.f, 0.f, 0.f,
|
|
0.f, 1.f, 0.f, 0.f,
|
|
0.f, 0.f, 1.f, 0.f,
|
|
0.f, 0.f, 0.f, 1.f
|
|
},
|
|
{
|
|
1.f, 0.f, 0.f, 0.f,
|
|
0.f, 1.f, 0.f, 0.f,
|
|
0.f, 0.f, 1.f, 0.f,
|
|
2.f, 0.f, 0.f, 1.f
|
|
},
|
|
{
|
|
1.f, 0.f, 0.f, 0.f,
|
|
0.f, 1.f, 0.f, 0.f,
|
|
0.f, 0.f, 1.f, 0.f,
|
|
2.f, 0.f, 2.f, 1.f
|
|
},
|
|
{
|
|
1.f, 0.f, 0.f, 0.f,
|
|
0.f, 1.f, 0.f, 0.f,
|
|
0.f, 0.f, 1.f, 0.f,
|
|
0.f, 0.f, 2.f, 1.f
|
|
}
|
|
};
|
|
|
|
private static final float[] IDENTITY_MATRIX = {
|
|
1.f, 0.f, 0.f, 0.f,
|
|
0.f, 1.f, 0.f, 0.f,
|
|
0.f, 0.f, 1.f, 0.f,
|
|
0.f, 0.f, 0.f, 1.f
|
|
};
|
|
|
|
private static final float[] EMPTY = new float[]{0};
|
|
|
|
private static final float[] INPUT_CAMERA_VIEW = {
|
|
1.f, 0.f, 0.f, 0.f,
|
|
0.f, 1.f, 0.f, 0.f,
|
|
0.f, 0.f, 1.f, 0.f,
|
|
0.f, 0.f, 0.f, 1.f
|
|
};
|
|
|
|
private static final float[] INPUT_BOUNDS = new float[]{-0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f};
|
|
private static final float[] INPUT_BOUNDS_SNAP = new float[]{1f, 1f, 1f};
|
|
|
|
private static final float[] INPUT_SNAP_VALUE = new float[]{1f, 1f, 1f};
|
|
private static final float[] INPUT_MATRIX_TRANSLATION = new float[3];
|
|
private static final float[] INPUT_MATRIX_SCALE = new float[3];
|
|
private static final float[] INPUT_MATRIX_ROTATION = new float[3];
|
|
|
|
private static final ImFloat INPUT_FLOAT = new ImFloat();
|
|
|
|
private static final ImBoolean BOUNDING_SIZE = new ImBoolean(false);
|
|
private static final ImBoolean USE_SNAP = new ImBoolean(false);
|
|
|
|
private static int currentMode = Mode.LOCAL;
|
|
private static int currentGizmoOperation;
|
|
|
|
private static boolean boundSizingSnap = false;
|
|
private static boolean firstFrame = true;
|
|
|
|
public static void show(final ImBoolean showImGuizmoWindow) {
|
|
ImGuizmo.beginFrame();
|
|
|
|
if (ImGui.begin("ImGuizmo Demo", showImGuizmoWindow)) {
|
|
ImGui.text("This a demo for ImGuizmo");
|
|
|
|
ImGui.alignTextToFramePadding();
|
|
ImGui.text("Repo:");
|
|
ImGui.sameLine();
|
|
if (ImGui.button(URL)) {
|
|
try {
|
|
Desktop.getDesktop().browse(new URI(URL));
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
ImGui.separator();
|
|
|
|
if (firstFrame) {
|
|
float[] eye = new float[]{
|
|
(float) (Math.cos(CAM_Y_ANGLE) * Math.cos(CAM_X_ANGLE) * CAM_DISTANCE),
|
|
(float) (Math.sin(CAM_X_ANGLE) * CAM_DISTANCE),
|
|
(float) (Math.sin(CAM_Y_ANGLE) * Math.cos(CAM_X_ANGLE) * CAM_DISTANCE)
|
|
};
|
|
float[] at = new float[]{0.f, 0.f, 0.f};
|
|
float[] up = new float[]{0.f, 1.f, 0.f};
|
|
lookAt(eye, at, up, INPUT_CAMERA_VIEW);
|
|
firstFrame = false;
|
|
}
|
|
|
|
ImGui.text("Keybindings:");
|
|
ImGui.text("T - Translate");
|
|
ImGui.text("R - Rotate");
|
|
ImGui.text("S - Scale");
|
|
ImGui.separator();
|
|
|
|
if (ImGuizmo.isUsing()) {
|
|
ImGui.text("Using gizmo");
|
|
if (ImGuizmo.isOver()) {
|
|
ImGui.text("Over a gizmo");
|
|
}
|
|
if (ImGuizmo.isOver(Operation.TRANSLATE)) {
|
|
ImGui.text("Over translate gizmo");
|
|
} else if (ImGuizmo.isOver(Operation.ROTATE)) {
|
|
ImGui.text("Over rotate gizmo");
|
|
} else if (ImGuizmo.isOver(Operation.SCALE)) {
|
|
ImGui.text("Over scale gizmo");
|
|
}
|
|
} else {
|
|
ImGui.text("Not using gizmo");
|
|
}
|
|
|
|
editTransform(showImGuizmoWindow);
|
|
ImGui.end();
|
|
}
|
|
}
|
|
|
|
private static void editTransform(final ImBoolean showImGuizmoWindow) {
|
|
if (ImGui.isKeyPressed(GLFW_KEY_T)) {
|
|
currentGizmoOperation = Operation.TRANSLATE;
|
|
} else if (ImGui.isKeyPressed(GLFW_KEY_R)) {
|
|
currentGizmoOperation = Operation.ROTATE;
|
|
} else if (ImGui.isKeyPressed(GLFW_KEY_S)) {
|
|
currentGizmoOperation = Operation.SCALE;
|
|
} else if (ImGui.isKeyPressed(GLFW_KEY_LEFT_SHIFT)) {
|
|
USE_SNAP.set(!USE_SNAP.get());
|
|
}
|
|
|
|
if (ImGuizmo.isUsing()) {
|
|
ImGuizmo.decomposeMatrixToComponents(OBJECT_MATRICES[0], INPUT_MATRIX_TRANSLATION, INPUT_MATRIX_ROTATION, INPUT_MATRIX_SCALE);
|
|
}
|
|
|
|
ImGui.inputFloat3("Tr", INPUT_MATRIX_TRANSLATION, "%.3f", ImGuiInputTextFlags.ReadOnly);
|
|
ImGui.inputFloat3("Rt", INPUT_MATRIX_ROTATION, "%.3f", ImGuiInputTextFlags.ReadOnly);
|
|
ImGui.inputFloat3("Sc", INPUT_MATRIX_SCALE, "%.3f", ImGuiInputTextFlags.ReadOnly);
|
|
|
|
if (ImGuizmo.isUsing()) {
|
|
ImGuizmo.recomposeMatrixFromComponents(INPUT_MATRIX_TRANSLATION, INPUT_MATRIX_ROTATION, INPUT_MATRIX_SCALE, OBJECT_MATRICES[0]);
|
|
}
|
|
|
|
if (currentGizmoOperation != Operation.SCALE) {
|
|
if (ImGui.radioButton("Local", currentMode == Mode.LOCAL)) {
|
|
currentMode = Mode.LOCAL;
|
|
}
|
|
ImGui.sameLine();
|
|
if (ImGui.radioButton("World", currentMode == Mode.WORLD)) {
|
|
currentMode = Mode.WORLD;
|
|
}
|
|
}
|
|
|
|
ImGui.checkbox("Snap Checkbox", USE_SNAP);
|
|
|
|
INPUT_FLOAT.set(INPUT_SNAP_VALUE[0]);
|
|
switch (currentGizmoOperation) {
|
|
case Operation.TRANSLATE:
|
|
ImGui.inputFloat3("Snap Value", INPUT_SNAP_VALUE);
|
|
break;
|
|
case Operation.ROTATE:
|
|
ImGui.inputFloat("Angle Value", INPUT_FLOAT);
|
|
float rotateValue = INPUT_FLOAT.get();
|
|
Arrays.fill(INPUT_SNAP_VALUE, rotateValue); //avoiding allocation
|
|
break;
|
|
case Operation.SCALE:
|
|
ImGui.inputFloat("Scale Value", INPUT_FLOAT);
|
|
float scaleValue = INPUT_FLOAT.get();
|
|
Arrays.fill(INPUT_SNAP_VALUE, scaleValue);
|
|
break;
|
|
}
|
|
|
|
ImGui.checkbox("Show Bound Sizing", BOUNDING_SIZE);
|
|
|
|
if (BOUNDING_SIZE.get()) {
|
|
if (ImGui.checkbox("BoundSizingSnap", boundSizingSnap)) {
|
|
boundSizingSnap = !boundSizingSnap;
|
|
}
|
|
ImGui.sameLine();
|
|
ImGui.inputFloat3("Snap", INPUT_BOUNDS_SNAP);
|
|
}
|
|
|
|
ImGui.setNextWindowPos(ImGui.getMainViewport().getPosX() + 100, ImGui.getMainViewport().getPosY() + 100, ImGuiCond.Once);
|
|
ImGui.setNextWindowSize(800, 400, ImGuiCond.Once);
|
|
ImGui.begin("Gizmo", showImGuizmoWindow);
|
|
ImGui.beginChild("prevent_window_from_moving_by_drag", 0, 0, false, ImGuiWindowFlags.NoMove);
|
|
|
|
float aspect = ImGui.getWindowWidth() / ImGui.getWindowHeight();
|
|
float[] cameraProjection = perspective(27, aspect, 0.1f, 100f);
|
|
|
|
ImGuizmo.setOrthographic(false);
|
|
ImGuizmo.enable(true);
|
|
ImGuizmo.setDrawList();
|
|
|
|
float windowWidth = ImGui.getWindowWidth();
|
|
float windowHeight = ImGui.getWindowHeight();
|
|
ImGuizmo.setRect(ImGui.getWindowPosX(), ImGui.getWindowPosY(), windowWidth, windowHeight);
|
|
|
|
ImGuizmo.drawGrid(INPUT_CAMERA_VIEW, cameraProjection, IDENTITY_MATRIX, 100);
|
|
ImGuizmo.setID(0);
|
|
ImGuizmo.drawCubes(INPUT_CAMERA_VIEW, cameraProjection, OBJECT_MATRICES[0]);
|
|
|
|
if (USE_SNAP.get() && BOUNDING_SIZE.get() && boundSizingSnap) {
|
|
ImGuizmo.manipulate(INPUT_CAMERA_VIEW, cameraProjection, currentGizmoOperation, currentMode, OBJECT_MATRICES[0], null, INPUT_SNAP_VALUE, INPUT_BOUNDS, INPUT_BOUNDS_SNAP);
|
|
} else if (USE_SNAP.get() && BOUNDING_SIZE.get()) {
|
|
ImGuizmo.manipulate(INPUT_CAMERA_VIEW, cameraProjection, currentGizmoOperation, currentMode, OBJECT_MATRICES[0], null, INPUT_SNAP_VALUE, INPUT_BOUNDS);
|
|
} else if (BOUNDING_SIZE.get() && boundSizingSnap) {
|
|
ImGuizmo.manipulate(INPUT_CAMERA_VIEW, cameraProjection, currentGizmoOperation, currentMode, OBJECT_MATRICES[0], null, EMPTY, INPUT_BOUNDS, INPUT_BOUNDS_SNAP);
|
|
} else if (BOUNDING_SIZE.get()) {
|
|
ImGuizmo.manipulate(INPUT_CAMERA_VIEW, cameraProjection, currentGizmoOperation, currentMode, OBJECT_MATRICES[0], null, EMPTY, INPUT_BOUNDS);
|
|
} else if (USE_SNAP.get()) {
|
|
ImGuizmo.manipulate(INPUT_CAMERA_VIEW, cameraProjection, currentGizmoOperation, currentMode, OBJECT_MATRICES[0], null, INPUT_SNAP_VALUE);
|
|
} else {
|
|
ImGuizmo.manipulate(INPUT_CAMERA_VIEW, cameraProjection, currentGizmoOperation, currentMode, OBJECT_MATRICES[0]);
|
|
}
|
|
|
|
float viewManipulateRight = ImGui.getWindowPosX() + windowWidth;
|
|
float viewManipulateTop = ImGui.getWindowPosY();
|
|
ImGuizmo.viewManipulate(INPUT_CAMERA_VIEW, CAM_DISTANCE, viewManipulateRight - 128, viewManipulateTop, 128, 128, 0x10101010);
|
|
|
|
ImGui.endChild();
|
|
ImGui.end();
|
|
}
|
|
|
|
private static float[] perspective(float fovY, float aspect, float near, float far) {
|
|
float ymax, xmax;
|
|
ymax = (float) (near * Math.tan(fovY * Math.PI / 180.0f));
|
|
xmax = ymax * aspect;
|
|
return frustum(-xmax, xmax, -ymax, ymax, near, far);
|
|
}
|
|
|
|
private static float[] frustum(float left, float right, float bottom, float top, float near, float far) {
|
|
float[] r = new float[16];
|
|
float temp = 2.0f * near;
|
|
float temp2 = right - left;
|
|
float temp3 = top - bottom;
|
|
float temp4 = far - near;
|
|
r[0] = temp / temp2;
|
|
r[1] = 0.0f;
|
|
r[2] = 0.0f;
|
|
r[3] = 0.0f;
|
|
r[4] = 0.0f;
|
|
r[5] = temp / temp3;
|
|
r[6] = 0.0f;
|
|
r[7] = 0.0f;
|
|
r[8] = (right + left) / temp2;
|
|
r[9] = (top + bottom) / temp3;
|
|
r[10] = (-far - near) / temp4;
|
|
r[11] = -1.0f;
|
|
r[12] = 0.0f;
|
|
r[13] = 0.0f;
|
|
r[14] = (-temp * far) / temp4;
|
|
r[15] = 0.0f;
|
|
return r;
|
|
}
|
|
|
|
private static float[] cross(float[] a, float[] b) {
|
|
float[] r = new float[3];
|
|
r[0] = a[1] * b[2] - a[2] * b[1];
|
|
r[1] = a[2] * b[0] - a[0] * b[2];
|
|
r[2] = a[0] * b[1] - a[1] * b[0];
|
|
return r;
|
|
}
|
|
|
|
private static float dot(float[] a, float[] b) {
|
|
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
}
|
|
|
|
private static float[] normalize(float[] a) {
|
|
float[] r = new float[3];
|
|
float il = (float) (1.f / (Math.sqrt(dot(a, a)) + FLT_EPSILON));
|
|
r[0] = a[0] * il;
|
|
r[1] = a[1] * il;
|
|
r[2] = a[2] * il;
|
|
return r;
|
|
}
|
|
|
|
private static void lookAt(float[] eye, float[] at, float[] up, float[] m16) {
|
|
float[] x;
|
|
float[] y;
|
|
float[] z;
|
|
float[] tmp = new float[3];
|
|
|
|
tmp[0] = eye[0] - at[0];
|
|
tmp[1] = eye[1] - at[1];
|
|
tmp[2] = eye[2] - at[2];
|
|
z = normalize(tmp);
|
|
y = normalize(up);
|
|
|
|
tmp = cross(y, z);
|
|
x = normalize(tmp);
|
|
|
|
tmp = cross(z, x);
|
|
y = normalize(tmp);
|
|
|
|
m16[0] = x[0];
|
|
m16[1] = y[0];
|
|
m16[2] = z[0];
|
|
m16[3] = 0.0f;
|
|
m16[4] = x[1];
|
|
m16[5] = y[1];
|
|
m16[6] = z[1];
|
|
m16[7] = 0.0f;
|
|
m16[8] = x[2];
|
|
m16[9] = y[2];
|
|
m16[10] = z[2];
|
|
m16[11] = 0.0f;
|
|
m16[12] = -dot(x, eye);
|
|
m16[13] = -dot(y, eye);
|
|
m16[14] = -dot(z, eye);
|
|
m16[15] = 1.0f;
|
|
}
|
|
}
|