Skip to content

Commit 4a863b7

Browse files
committed
feat: add local HTTP server and Swing control panel for remote model interaction
1 parent 78c0c36 commit 4a863b7

2 files changed

Lines changed: 74 additions & 26 deletions

File tree

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package dev.eatgrapes.live2d.example;
2+
3+
import javax.swing.*;
4+
import java.awt.*;
5+
import java.net.URI;
6+
import java.net.http.HttpClient;
7+
import java.net.http.HttpRequest;
8+
import java.net.http.HttpResponse;
9+
10+
public class ControlPanel {
11+
private static final HttpClient client = HttpClient.newHttpClient();
12+
13+
public static void show() {
14+
SwingUtilities.invokeLater(() -> {
15+
JFrame frame = new JFrame("Live2D Controller");
16+
frame.setLayout(new FlowLayout());
17+
frame.setSize(300, 200);
18+
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
19+
20+
addButton(frame, "Idle Motion", "m01");
21+
addButton(frame, "Tap Body", "m04");
22+
23+
frame.setVisible(true);
24+
});
25+
}
26+
27+
private static void addButton(JFrame frame, String label, String motionId) {
28+
JButton btn = new JButton(label);
29+
btn.addActionListener(e -> sendRequest("/motion?id=" + motionId));
30+
frame.add(btn);
31+
}
32+
33+
private static void sendRequest(String path) {
34+
try {
35+
HttpRequest request = HttpRequest.newBuilder()
36+
.uri(URI.create("http://localhost:8080" + path))
37+
.GET()
38+
.build();
39+
client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
40+
} catch (Exception e) {
41+
e.printStackTrace();
42+
}
43+
}
44+
}

example/src/main/java/dev/eatgrapes/live2d/example/Main.java

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package dev.eatgrapes.live2d.example;
22

3+
import com.sun.net.httpserver.HttpServer;
34
import dev.eatgrapes.live2d.CubismFramework;
45
import dev.eatgrapes.live2d.CubismUserModel;
56
import org.lwjgl.opengl.GL;
67
import org.lwjgl.stb.STBImage;
78
import org.lwjgl.system.MemoryStack;
89

910
import java.io.InputStream;
11+
import java.net.InetSocketAddress;
1012
import java.nio.ByteBuffer;
1113
import java.nio.DoubleBuffer;
1214
import java.nio.IntBuffer;
@@ -15,6 +17,7 @@
1517
import java.nio.file.StandardCopyOption;
1618
import java.util.HashMap;
1719
import java.util.Map;
20+
import java.util.concurrent.ConcurrentLinkedQueue;
1821

1922
import static org.lwjgl.glfw.GLFW.*;
2023
import static org.lwjgl.opengl.GL11.*;
@@ -25,17 +28,38 @@ public class Main {
2528
private CubismUserModel model;
2629
private final Map<String, byte[]> motions = new HashMap<>();
2730
private final float[] mvp = new float[]{1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1};
31+
private final ConcurrentLinkedQueue<Runnable> taskQueue = new ConcurrentLinkedQueue<>();
2832

2933
public void run() throws Exception {
3034
init();
3135
setup();
36+
startServer();
37+
ControlPanel.show();
3238
loop();
3339
cleanup();
3440
}
3541

42+
private void startServer() throws Exception {
43+
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
44+
server.createContext("/motion", t -> {
45+
String query = t.getRequestURI().getQuery();
46+
String id = query.split("=")[1];
47+
taskQueue.add(() -> {
48+
try {
49+
model.startMotion(motions.get(id), 3, false, null);
50+
} catch (Exception e) { e.printStackTrace(); }
51+
});
52+
t.sendResponseHeaders(200, 0);
53+
t.close();
54+
});
55+
server.setExecutor(null);
56+
server.start();
57+
System.out.println("Control server started on port 8080");
58+
}
59+
3660
private void init() {
3761
if (!glfwInit()) throw new RuntimeException("GLFW failed");
38-
window = glfwCreateWindow(800, 800, "Live2D Interaction", 0, 0);
62+
window = glfwCreateWindow(800, 800, "Live2D Example", 0, 0);
3963
glfwMakeContextCurrent(window);
4064
GL.createCapabilities();
4165
glfwSwapInterval(1);
@@ -52,47 +76,28 @@ private void init() {
5276
if (model != null) model.setDragging(nx, ny);
5377
}
5478
});
55-
56-
glfwSetMouseButtonCallback(window, (win, button, action, mods) -> {
57-
if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {
58-
try (MemoryStack s = MemoryStack.stackPush()) {
59-
DoubleBuffer xb = s.mallocDouble(1), yb = s.mallocDouble(1);
60-
IntBuffer wb = s.mallocInt(1), hb = s.mallocInt(1);
61-
glfwGetCursorPos(win, xb, yb);
62-
glfwGetWindowSize(win, wb, hb);
63-
float aspect = (float) wb.get(0) / hb.get(0);
64-
float nx = (float) (xb.get(0) / (wb.get(0) / 2.0) - 1.0) * aspect;
65-
float ny = (float) (1.0 - yb.get(0) / (hb.get(0) / 2.0));
66-
67-
if (model.isHit("HitArea", nx, ny)) {
68-
System.out.println("Hit Body!");
69-
model.startMotion(motions.get("m04"), 3, false, null);
70-
}
71-
} catch (Exception e) { e.printStackTrace(); }
72-
}
73-
});
7479
}
7580

7681
private void setup() throws Exception {
7782
CubismFramework.startUp();
7883
CubismFramework.initialize();
79-
8084
model = new CubismUserModel();
8185
model.loadModel(load("/model/Hiyori/Hiyori.moc3"));
8286
model.loadPose(load("/model/Hiyori/Hiyori.pose3.json"));
8387
model.loadPhysics(load("/model/Hiyori/Hiyori.physics3.json"));
8488
model.createRenderer();
85-
8689
model.registerTexture(0, loadTex("/model/Hiyori/Hiyori.2048/texture_00.png"));
8790
model.registerTexture(1, loadTex("/model/Hiyori/Hiyori.2048/texture_01.png"));
88-
91+
motions.put("m01", load("/model/Hiyori/motions/Hiyori_m01.motion3.json"));
8992
motions.put("m04", load("/model/Hiyori/motions/Hiyori_m04.motion3.json"));
9093
}
9194

9295
private void loop() {
9396
while (!glfwWindowShouldClose(window)) {
97+
Runnable task;
98+
while ((task = taskQueue.poll()) != null) task.run();
99+
94100
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
95-
96101
try (MemoryStack s = MemoryStack.stackPush()) {
97102
IntBuffer w = s.mallocInt(1), h = s.mallocInt(1);
98103
glfwGetWindowSize(window, w, h);
@@ -101,7 +106,6 @@ private void loop() {
101106
for (int i = 0; i < 16; i++) mvp[i] = 0;
102107
mvp[0] = 1.0f / aspect; mvp[5] = 1.0f; mvp[10] = 1.0f; mvp[15] = 1.0f;
103108
}
104-
105109
model.update(0.016f);
106110
model.draw(mvp);
107111
glfwSwapBuffers(window);
@@ -142,4 +146,4 @@ private int loadTex(String p) throws Exception {
142146
}
143147

144148
public static void main(String[] args) throws Exception { new Main().run(); }
145-
}
149+
}

0 commit comments

Comments
 (0)