|
| 1 | +import imgui.ImGui; |
| 2 | +import imgui.extension.imguizmo.ImGuizmo; |
| 3 | +import imgui.extension.imguizmo.flag.Mode; |
| 4 | +import imgui.extension.imguizmo.flag.Operation; |
| 5 | +import imgui.flag.ImGuiCond; |
| 6 | +import imgui.flag.ImGuiInputTextFlags; |
| 7 | +import imgui.flag.ImGuiWindowFlags; |
| 8 | +import imgui.type.ImBoolean; |
| 9 | +import imgui.type.ImFloat; |
| 10 | + |
| 11 | +import java.awt.Desktop; |
| 12 | +import java.net.URI; |
| 13 | +import java.util.Arrays; |
| 14 | + |
| 15 | +import static org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT_SHIFT; |
| 16 | +import static org.lwjgl.glfw.GLFW.GLFW_KEY_R; |
| 17 | +import static org.lwjgl.glfw.GLFW.GLFW_KEY_S; |
| 18 | +import static org.lwjgl.glfw.GLFW.GLFW_KEY_T; |
| 19 | + |
| 20 | +public class ExampleImGuizmo { |
| 21 | + private static final String URL = "https://github.com/CedricGuillemet/ImGuizmo/tree/f7bbbe"; |
| 22 | + |
| 23 | + private static final int CAM_DISTANCE = 8; |
| 24 | + private static final float CAM_Y_ANGLE = 165.f / 180.f * (float) Math.PI; |
| 25 | + private static final float CAM_X_ANGLE = 32.f / 180.f * (float) Math.PI; |
| 26 | + private static final float FLT_EPSILON = 1.19209290E-07f; |
| 27 | + |
| 28 | + private static final float[][] OBJECT_MATRICES = { |
| 29 | + { |
| 30 | + 1.f, 0.f, 0.f, 0.f, |
| 31 | + 0.f, 1.f, 0.f, 0.f, |
| 32 | + 0.f, 0.f, 1.f, 0.f, |
| 33 | + 0.f, 0.f, 0.f, 1.f |
| 34 | + }, |
| 35 | + { |
| 36 | + 1.f, 0.f, 0.f, 0.f, |
| 37 | + 0.f, 1.f, 0.f, 0.f, |
| 38 | + 0.f, 0.f, 1.f, 0.f, |
| 39 | + 2.f, 0.f, 0.f, 1.f |
| 40 | + }, |
| 41 | + { |
| 42 | + 1.f, 0.f, 0.f, 0.f, |
| 43 | + 0.f, 1.f, 0.f, 0.f, |
| 44 | + 0.f, 0.f, 1.f, 0.f, |
| 45 | + 2.f, 0.f, 2.f, 1.f |
| 46 | + }, |
| 47 | + { |
| 48 | + 1.f, 0.f, 0.f, 0.f, |
| 49 | + 0.f, 1.f, 0.f, 0.f, |
| 50 | + 0.f, 0.f, 1.f, 0.f, |
| 51 | + 0.f, 0.f, 2.f, 1.f |
| 52 | + } |
| 53 | + }; |
| 54 | + |
| 55 | + private static final float[] IDENTITY_MATRIX = { |
| 56 | + 1.f, 0.f, 0.f, 0.f, |
| 57 | + 0.f, 1.f, 0.f, 0.f, |
| 58 | + 0.f, 0.f, 1.f, 0.f, |
| 59 | + 0.f, 0.f, 0.f, 1.f |
| 60 | + }; |
| 61 | + |
| 62 | + private static final float[] VIEW_MANIPULATE_SIZE = new float[]{128f, 128f}; |
| 63 | + |
| 64 | + private static final float[] EMPTY = new float[]{0}; |
| 65 | + |
| 66 | + private static final float[] INPUT_CAMERA_VIEW = { |
| 67 | + 1.f, 0.f, 0.f, 0.f, |
| 68 | + 0.f, 1.f, 0.f, 0.f, |
| 69 | + 0.f, 0.f, 1.f, 0.f, |
| 70 | + 0.f, 0.f, 0.f, 1.f |
| 71 | + }; |
| 72 | + |
| 73 | + private static final float[] INPUT_BOUNDS = new float[]{-0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f}; |
| 74 | + private static final float[] INPUT_BOUNDS_SNAP = new float[]{1f, 1f, 1f}; |
| 75 | + |
| 76 | + private static final float[] INPUT_SNAP_VALUE = new float[]{1f, 1f, 1f}; |
| 77 | + private static final float[] INPUT_MATRIX_TRANSLATION = new float[3]; |
| 78 | + private static final float[] INPUT_MATRIX_SCALE = new float[3]; |
| 79 | + private static final float[] INPUT_MATRIX_ROTATION = new float[3]; |
| 80 | + |
| 81 | + private static final ImFloat INPUT_FLOAT = new ImFloat(); |
| 82 | + |
| 83 | + private static final ImBoolean BOUNDING_SIZE = new ImBoolean(false); |
| 84 | + private static final ImBoolean USE_SNAP = new ImBoolean(false); |
| 85 | + |
| 86 | + private static int currentMode = Mode.LOCAL; |
| 87 | + private static int currentGizmoOperation; |
| 88 | + |
| 89 | + private static boolean boundSizingSnap = false; |
| 90 | + private static boolean firstFrame = true; |
| 91 | + |
| 92 | + public static void show(final ImBoolean showImGuizmoWindow) { |
| 93 | + ImGuizmo.beginFrame(); |
| 94 | + |
| 95 | + if (ImGui.begin("ImGuizmo Demo", showImGuizmoWindow)) { |
| 96 | + ImGui.text("This a demo for ImGuizmo"); |
| 97 | + |
| 98 | + ImGui.alignTextToFramePadding(); |
| 99 | + ImGui.text("Repo:"); |
| 100 | + ImGui.sameLine(); |
| 101 | + if (ImGui.button(URL)) { |
| 102 | + try { |
| 103 | + Desktop.getDesktop().browse(new URI(URL)); |
| 104 | + } catch (Exception e) { |
| 105 | + e.printStackTrace(); |
| 106 | + } |
| 107 | + } |
| 108 | + ImGui.separator(); |
| 109 | + |
| 110 | + if (firstFrame) { |
| 111 | + float[] eye = new float[]{ |
| 112 | + (float) (Math.cos(CAM_Y_ANGLE) * Math.cos(CAM_X_ANGLE) * CAM_DISTANCE), |
| 113 | + (float) (Math.sin(CAM_X_ANGLE) * CAM_DISTANCE), |
| 114 | + (float) (Math.sin(CAM_Y_ANGLE) * Math.cos(CAM_X_ANGLE) * CAM_DISTANCE) |
| 115 | + }; |
| 116 | + float[] at = new float[]{0.f, 0.f, 0.f}; |
| 117 | + float[] up = new float[]{0.f, 1.f, 0.f}; |
| 118 | + lookAt(eye, at, up, INPUT_CAMERA_VIEW); |
| 119 | + firstFrame = false; |
| 120 | + } |
| 121 | + |
| 122 | + ImGui.text("Keybindings:"); |
| 123 | + ImGui.text("T - Translate"); |
| 124 | + ImGui.text("R - Rotate"); |
| 125 | + ImGui.text("S - Scale"); |
| 126 | + ImGui.separator(); |
| 127 | + |
| 128 | + if (ImGuizmo.isUsing()) { |
| 129 | + ImGui.text("Using gizmo"); |
| 130 | + if (ImGuizmo.isOver()) { |
| 131 | + ImGui.text("Over a gizmo"); |
| 132 | + } |
| 133 | + if (ImGuizmo.isOver(Operation.TRANSLATE)) { |
| 134 | + ImGui.text("Over translate gizmo"); |
| 135 | + } else if (ImGuizmo.isOver(Operation.ROTATE)) { |
| 136 | + ImGui.text("Over rotate gizmo"); |
| 137 | + } else if (ImGuizmo.isOver(Operation.SCALE)) { |
| 138 | + ImGui.text("Over scale gizmo"); |
| 139 | + } |
| 140 | + } else { |
| 141 | + ImGui.text("Not using gizmo"); |
| 142 | + } |
| 143 | + |
| 144 | + editTransform(showImGuizmoWindow); |
| 145 | + ImGui.end(); |
| 146 | + } |
| 147 | + } |
| 148 | + |
| 149 | + private static void editTransform(final ImBoolean showImGuizmoWindow) { |
| 150 | + if (ImGui.isKeyPressed(GLFW_KEY_T)) { |
| 151 | + currentGizmoOperation = Operation.TRANSLATE; |
| 152 | + } else if (ImGui.isKeyPressed(GLFW_KEY_R)) { |
| 153 | + currentGizmoOperation = Operation.ROTATE; |
| 154 | + } else if (ImGui.isKeyPressed(GLFW_KEY_S)) { |
| 155 | + currentGizmoOperation = Operation.SCALE; |
| 156 | + } else if (ImGui.isKeyPressed(GLFW_KEY_LEFT_SHIFT)) { |
| 157 | + USE_SNAP.set(!USE_SNAP.get()); |
| 158 | + } |
| 159 | + |
| 160 | + if (ImGuizmo.isUsing()) { |
| 161 | + ImGuizmo.decomposeMatrixToComponents(OBJECT_MATRICES[0], INPUT_MATRIX_TRANSLATION, INPUT_MATRIX_ROTATION, INPUT_MATRIX_SCALE); |
| 162 | + } |
| 163 | + |
| 164 | + ImGui.inputFloat3("Tr", INPUT_MATRIX_TRANSLATION, "%.3f", ImGuiInputTextFlags.ReadOnly); |
| 165 | + ImGui.inputFloat3("Rt", INPUT_MATRIX_ROTATION, "%.3f", ImGuiInputTextFlags.ReadOnly); |
| 166 | + ImGui.inputFloat3("Sc", INPUT_MATRIX_SCALE, "%.3f", ImGuiInputTextFlags.ReadOnly); |
| 167 | + |
| 168 | + if (ImGuizmo.isUsing()) { |
| 169 | + ImGuizmo.recomposeMatrixFromComponents(OBJECT_MATRICES[0], INPUT_MATRIX_TRANSLATION, INPUT_MATRIX_ROTATION, INPUT_MATRIX_SCALE); |
| 170 | + } |
| 171 | + |
| 172 | + if (currentGizmoOperation != Operation.SCALE) { |
| 173 | + if (ImGui.radioButton("Local", currentMode == Mode.LOCAL)) { |
| 174 | + currentMode = Mode.LOCAL; |
| 175 | + } |
| 176 | + ImGui.sameLine(); |
| 177 | + if (ImGui.radioButton("World", currentMode == Mode.WORLD)) { |
| 178 | + currentMode = Mode.WORLD; |
| 179 | + } |
| 180 | + } |
| 181 | + |
| 182 | + ImGui.checkbox("Snap Checkbox", USE_SNAP); |
| 183 | + |
| 184 | + INPUT_FLOAT.set(INPUT_SNAP_VALUE[0]); |
| 185 | + switch (currentGizmoOperation) { |
| 186 | + case Operation.TRANSLATE: |
| 187 | + ImGui.inputFloat3("Snap Value", INPUT_SNAP_VALUE); |
| 188 | + break; |
| 189 | + case Operation.ROTATE: |
| 190 | + ImGui.inputFloat("Angle Value", INPUT_FLOAT); |
| 191 | + float rotateValue = INPUT_FLOAT.get(); |
| 192 | + Arrays.fill(INPUT_SNAP_VALUE, rotateValue); //avoiding allocation |
| 193 | + break; |
| 194 | + case Operation.SCALE: |
| 195 | + ImGui.inputFloat("Scale Value", INPUT_FLOAT); |
| 196 | + float scaleValue = INPUT_FLOAT.get(); |
| 197 | + Arrays.fill(INPUT_SNAP_VALUE, scaleValue); |
| 198 | + break; |
| 199 | + } |
| 200 | + |
| 201 | + ImGui.checkbox("Show Bound Sizing", BOUNDING_SIZE); |
| 202 | + |
| 203 | + if (BOUNDING_SIZE.get()) { |
| 204 | + if (ImGui.checkbox("BoundSizingSnap", boundSizingSnap)) { |
| 205 | + boundSizingSnap = !boundSizingSnap; |
| 206 | + } |
| 207 | + ImGui.sameLine(); |
| 208 | + ImGui.inputFloat3("Snap", INPUT_BOUNDS_SNAP); |
| 209 | + } |
| 210 | + |
| 211 | + ImGui.setNextWindowPos(ImGui.getMainViewport().getPosX() + 100, ImGui.getMainViewport().getPosY() + 100, ImGuiCond.Once); |
| 212 | + ImGui.setNextWindowSize(800, 400, ImGuiCond.Once); |
| 213 | + ImGui.begin("Gizmo", showImGuizmoWindow); |
| 214 | + ImGui.beginChild("prevent_window_from_moving_by_drag", 0, 0, false, ImGuiWindowFlags.NoMove); |
| 215 | + |
| 216 | + float aspect = ImGui.getWindowWidth() / ImGui.getWindowHeight(); |
| 217 | + float[] cameraProjection = perspective(27, aspect, 0.1f, 100f); |
| 218 | + |
| 219 | + ImGuizmo.setOrthographic(false); |
| 220 | + ImGuizmo.setEnabled(true); |
| 221 | + ImGuizmo.setDrawList(); |
| 222 | + |
| 223 | + float windowWidth = ImGui.getWindowWidth(); |
| 224 | + float windowHeight = ImGui.getWindowHeight(); |
| 225 | + ImGuizmo.setRect(ImGui.getWindowPosX(), ImGui.getWindowPosY(), windowWidth, windowHeight); |
| 226 | + |
| 227 | + ImGuizmo.drawGrid(INPUT_CAMERA_VIEW, cameraProjection, IDENTITY_MATRIX, 100); |
| 228 | + ImGuizmo.setId(0); |
| 229 | + ImGuizmo.drawCubes(INPUT_CAMERA_VIEW, cameraProjection, OBJECT_MATRICES[0]); |
| 230 | + |
| 231 | + if (USE_SNAP.get() && BOUNDING_SIZE.get() && boundSizingSnap) { |
| 232 | + ImGuizmo.manipulate(INPUT_CAMERA_VIEW, cameraProjection, OBJECT_MATRICES[0], currentGizmoOperation, currentMode, INPUT_SNAP_VALUE, INPUT_BOUNDS, INPUT_BOUNDS_SNAP); |
| 233 | + } else if (USE_SNAP.get() && BOUNDING_SIZE.get()) { |
| 234 | + ImGuizmo.manipulate(INPUT_CAMERA_VIEW, cameraProjection, OBJECT_MATRICES[0], currentGizmoOperation, currentMode, INPUT_SNAP_VALUE, INPUT_BOUNDS); |
| 235 | + } else if (BOUNDING_SIZE.get() && boundSizingSnap) { |
| 236 | + ImGuizmo.manipulate(INPUT_CAMERA_VIEW, cameraProjection, OBJECT_MATRICES[0], currentGizmoOperation, currentMode, EMPTY, INPUT_BOUNDS, INPUT_BOUNDS_SNAP); |
| 237 | + } else if (BOUNDING_SIZE.get()) { |
| 238 | + ImGuizmo.manipulate(INPUT_CAMERA_VIEW, cameraProjection, OBJECT_MATRICES[0], currentGizmoOperation, currentMode, EMPTY, INPUT_BOUNDS); |
| 239 | + } else if (USE_SNAP.get()) { |
| 240 | + ImGuizmo.manipulate(INPUT_CAMERA_VIEW, cameraProjection, OBJECT_MATRICES[0], currentGizmoOperation, currentMode, INPUT_SNAP_VALUE); |
| 241 | + } else { |
| 242 | + ImGuizmo.manipulate(INPUT_CAMERA_VIEW, cameraProjection, OBJECT_MATRICES[0], currentGizmoOperation, currentMode); |
| 243 | + } |
| 244 | + |
| 245 | + float viewManipulateRight = ImGui.getWindowPosX() + windowWidth; |
| 246 | + float viewManipulateTop = ImGui.getWindowPosY(); |
| 247 | + ImGuizmo.viewManipulate(INPUT_CAMERA_VIEW, CAM_DISTANCE, new float[]{viewManipulateRight - 128, viewManipulateTop}, VIEW_MANIPULATE_SIZE, 0x10101010); |
| 248 | + |
| 249 | + ImGui.endChild(); |
| 250 | + ImGui.end(); |
| 251 | + } |
| 252 | + |
| 253 | + private static float[] perspective(float fovY, float aspect, float near, float far) { |
| 254 | + float ymax, xmax; |
| 255 | + ymax = (float) (near * Math.tan(fovY * Math.PI / 180.0f)); |
| 256 | + xmax = ymax * aspect; |
| 257 | + return frustum(-xmax, xmax, -ymax, ymax, near, far); |
| 258 | + } |
| 259 | + |
| 260 | + private static float[] frustum(float left, float right, float bottom, float top, float near, float far) { |
| 261 | + float[] r = new float[16]; |
| 262 | + float temp = 2.0f * near; |
| 263 | + float temp2 = right - left; |
| 264 | + float temp3 = top - bottom; |
| 265 | + float temp4 = far - near; |
| 266 | + r[0] = temp / temp2; |
| 267 | + r[1] = 0.0f; |
| 268 | + r[2] = 0.0f; |
| 269 | + r[3] = 0.0f; |
| 270 | + r[4] = 0.0f; |
| 271 | + r[5] = temp / temp3; |
| 272 | + r[6] = 0.0f; |
| 273 | + r[7] = 0.0f; |
| 274 | + r[8] = (right + left) / temp2; |
| 275 | + r[9] = (top + bottom) / temp3; |
| 276 | + r[10] = (-far - near) / temp4; |
| 277 | + r[11] = -1.0f; |
| 278 | + r[12] = 0.0f; |
| 279 | + r[13] = 0.0f; |
| 280 | + r[14] = (-temp * far) / temp4; |
| 281 | + r[15] = 0.0f; |
| 282 | + return r; |
| 283 | + } |
| 284 | + |
| 285 | + private static float[] cross(float[] a, float[] b) { |
| 286 | + float[] r = new float[3]; |
| 287 | + r[0] = a[1] * b[2] - a[2] * b[1]; |
| 288 | + r[1] = a[2] * b[0] - a[0] * b[2]; |
| 289 | + r[2] = a[0] * b[1] - a[1] * b[0]; |
| 290 | + return r; |
| 291 | + } |
| 292 | + |
| 293 | + private static float dot(float[] a, float[] b) { |
| 294 | + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; |
| 295 | + } |
| 296 | + |
| 297 | + private static float[] normalize(float[] a) { |
| 298 | + float[] r = new float[3]; |
| 299 | + float il = (float) (1.f / (Math.sqrt(dot(a, a)) + FLT_EPSILON)); |
| 300 | + r[0] = a[0] * il; |
| 301 | + r[1] = a[1] * il; |
| 302 | + r[2] = a[2] * il; |
| 303 | + return r; |
| 304 | + } |
| 305 | + |
| 306 | + private static void lookAt(float[] eye, float[] at, float[] up, float[] m16) { |
| 307 | + float[] x; |
| 308 | + float[] y; |
| 309 | + float[] z; |
| 310 | + float[] tmp = new float[3]; |
| 311 | + |
| 312 | + tmp[0] = eye[0] - at[0]; |
| 313 | + tmp[1] = eye[1] - at[1]; |
| 314 | + tmp[2] = eye[2] - at[2]; |
| 315 | + z = normalize(tmp); |
| 316 | + y = normalize(up); |
| 317 | + |
| 318 | + tmp = cross(y, z); |
| 319 | + x = normalize(tmp); |
| 320 | + |
| 321 | + tmp = cross(z, x); |
| 322 | + y = normalize(tmp); |
| 323 | + |
| 324 | + m16[0] = x[0]; |
| 325 | + m16[1] = y[0]; |
| 326 | + m16[2] = z[0]; |
| 327 | + m16[3] = 0.0f; |
| 328 | + m16[4] = x[1]; |
| 329 | + m16[5] = y[1]; |
| 330 | + m16[6] = z[1]; |
| 331 | + m16[7] = 0.0f; |
| 332 | + m16[8] = x[2]; |
| 333 | + m16[9] = y[2]; |
| 334 | + m16[10] = z[2]; |
| 335 | + m16[11] = 0.0f; |
| 336 | + m16[12] = -dot(x, eye); |
| 337 | + m16[13] = -dot(y, eye); |
| 338 | + m16[14] = -dot(z, eye); |
| 339 | + m16[15] = 1.0f; |
| 340 | + } |
| 341 | +} |
0 commit comments