Skip to content

Commit 41b4d95

Browse files
committed
fix: remove excessive idle motion trigger in loop
1 parent a89b775 commit 41b4d95

4 files changed

Lines changed: 67 additions & 14 deletions

File tree

binding/src/main/java/dev/eatgrapes/live2d/CubismUserModel.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ public CubismUserModel() {
2525
public void loadExpression(byte[] buffer, String name) { loadExpressionNative(_ptr, buffer, name); }
2626
private static native void loadExpressionNative(long ptr, byte[] buffer, String name);
2727

28+
public void setExpression(String name) { setExpressionNative(_ptr, name); }
29+
private static native void setExpressionNative(long ptr, String name);
30+
2831
public void createRenderer() { createRendererNative(_ptr); }
2932
private static native void createRendererNative(long ptr);
3033

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

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,35 @@ public class ControlPanel {
1313
public static void show() {
1414
SwingUtilities.invokeLater(() -> {
1515
JFrame frame = new JFrame("Live2D Controller");
16-
frame.setLayout(new FlowLayout());
17-
frame.setSize(300, 200);
16+
frame.setLayout(new GridLayout(0, 1, 10, 10));
17+
frame.setSize(300, 300);
1818
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
1919

20-
addButton(frame, "Idle Motion", "m01");
21-
addButton(frame, "Tap Body", "m04");
20+
JPanel motionPanel = new JPanel(new FlowLayout());
21+
motionPanel.setBorder(BorderFactory.createTitledBorder("Motions"));
22+
addMotionButton(motionPanel, "Idle", "idle");
23+
addMotionButton(motionPanel, "Tap Body", "tap_body");
24+
frame.add(motionPanel);
25+
26+
JPanel exprPanel = new JPanel(new FlowLayout());
27+
exprPanel.setBorder(BorderFactory.createTitledBorder("Expressions"));
28+
addExprButton(exprPanel, "Default", "Default");
29+
frame.add(exprPanel);
2230

2331
frame.setVisible(true);
2432
});
2533
}
2634

27-
private static void addButton(JFrame frame, String label, String motionId) {
35+
private static void addMotionButton(JPanel panel, String label, String id) {
36+
JButton btn = new JButton(label);
37+
btn.addActionListener(e -> sendRequest("/motion?id=" + id));
38+
panel.add(btn);
39+
}
40+
41+
private static void addExprButton(JPanel panel, String label, String name) {
2842
JButton btn = new JButton(label);
29-
btn.addActionListener(e -> sendRequest("/motion?id=" + motionId));
30-
frame.add(btn);
43+
btn.addActionListener(e -> sendRequest("/expression?name=" + name));
44+
panel.add(btn);
3145
}
3246

3347
private static void sendRequest(String path) {
@@ -41,4 +55,4 @@ private static void sendRequest(String path) {
4155
e.printStackTrace();
4256
}
4357
}
44-
}
58+
}

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

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,20 @@ private void startServer() throws Exception {
4545
String query = t.getRequestURI().getQuery();
4646
String id = query.split("=")[1];
4747
taskQueue.add(() -> {
48-
try {
48+
if (motions.containsKey(id)) {
4949
model.startMotion(motions.get(id), 3, false, null);
50-
} catch (Exception e) { e.printStackTrace(); }
50+
}
5151
});
5252
t.sendResponseHeaders(200, 0);
5353
t.close();
5454
});
55+
server.createContext("/expression", t -> {
56+
String query = t.getRequestURI().getQuery();
57+
String name = query.split("=")[1];
58+
taskQueue.add(() -> model.setExpression(name));
59+
t.sendResponseHeaders(200, 0);
60+
t.close();
61+
});
5562
server.setExecutor(null);
5663
server.start();
5764
System.out.println("Control server started on port 8080");
@@ -88,8 +95,8 @@ private void setup() throws Exception {
8895
model.createRenderer();
8996
model.registerTexture(0, loadTex("/model/Hiyori/Hiyori.2048/texture_00.png"));
9097
model.registerTexture(1, loadTex("/model/Hiyori/Hiyori.2048/texture_01.png"));
91-
motions.put("m01", load("/model/Hiyori/motions/Hiyori_m01.motion3.json"));
92-
motions.put("m04", load("/model/Hiyori/motions/Hiyori_m04.motion3.json"));
98+
motions.put("idle", load("/model/Hiyori/motions/Hiyori_m01.motion3.json"));
99+
motions.put("tap_body", load("/model/Hiyori/motions/Hiyori_m04.motion3.json"));
93100
}
94101

95102
private void loop() {
@@ -146,4 +153,4 @@ private int loadTex(String p) throws Exception {
146153
}
147154

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

native/src/CubismUserModel_JNI.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <Id/CubismIdManager.hpp>
44
#include <Rendering/OpenGL/CubismRenderer_OpenGLES2.hpp>
55
#include <Motion/CubismMotion.hpp>
6+
#include <Motion/CubismExpressionMotion.hpp>
67
#include <vector>
78
#include <string>
89
#include <map>
@@ -26,6 +27,7 @@ class JniUserModel : public CubismUserModel {
2627
JNIEnv* env = getEnv();
2728
if (env && _javaObj) env->DeleteGlobalRef(_javaObj);
2829
for (auto& it : _motionBuffers) CubismMotion::Delete(it.first);
30+
for (auto& it : _expressions) ACubismMotion::Delete(it.second);
2931
}
3032

3133
void loadModelCopy(const csmByte* buffer, csmSizeInt size) {
@@ -44,6 +46,21 @@ class JniUserModel : public CubismUserModel {
4446
LoadPose(_poseBuffer.data(), (csmSizeInt)_poseBuffer.size());
4547
}
4648

49+
void loadExpressionCopy(const csmByte* buffer, csmSizeInt size, const std::string& name) {
50+
std::vector<csmByte> exprBuf(buffer, buffer + size);
51+
auto* expr = LoadExpression(exprBuf.data(), (csmSizeInt)exprBuf.size(), name.c_str());
52+
if (expr) {
53+
_expressions[name] = expr;
54+
_expressionBuffers[name] = std::move(exprBuf);
55+
}
56+
}
57+
58+
void setExpression(const std::string& name) {
59+
if (_expressions.count(name)) {
60+
_expressionManager->StartMotionPriority(_expressions[name], false, 3);
61+
}
62+
}
63+
4764
void startMotionCopy(const csmByte* buffer, csmSizeInt size, int priority, bool loop) {
4865
std::vector<csmByte> motionBuf(buffer, buffer + size);
4966
auto* motion = CubismMotion::Create(motionBuf.data(), (csmSizeInt)motionBuf.size());
@@ -87,6 +104,10 @@ class JniUserModel : public CubismUserModel {
87104
_motionManager->UpdateMotion(_model, dt);
88105
_model->SaveParameters();
89106

107+
if (_expressionManager) {
108+
_expressionManager->UpdateMotion(_model, dt);
109+
}
110+
90111
if (_pose) _pose->UpdateParameters(_model, dt);
91112

92113
if (_dragManager) {
@@ -130,6 +151,8 @@ class JniUserModel : public CubismUserModel {
130151
jobject _javaObj = nullptr;
131152
std::vector<csmByte> _mocBuffer, _physicsBuffer, _poseBuffer;
132153
std::map<CubismMotion*, std::vector<csmByte>> _motionBuffers;
154+
std::map<std::string, std::vector<csmByte>> _expressionBuffers;
155+
std::map<std::string, ACubismMotion*> _expressions;
133156
std::vector<CubismMotion*> _pendingDeletion;
134157
std::mutex _pendingMutex;
135158
};
@@ -176,11 +199,17 @@ JNIEXPORT void JNICALL Java_dev_eatgrapes_live2d_CubismUserModel_loadExpressionN
176199
const char* n = env->GetStringUTFChars(name, nullptr);
177200
jsize len = env->GetArrayLength(buffer);
178201
jbyte* data = env->GetByteArrayElements(buffer, nullptr);
179-
((JniUserModel*)ptr)->LoadExpression((const csmByte*)data, len, n);
202+
((JniUserModel*)ptr)->loadExpressionCopy((const csmByte*)data, len, n);
180203
env->ReleaseByteArrayElements(buffer, data, JNI_ABORT);
181204
env->ReleaseStringUTFChars(name, n);
182205
}
183206

207+
JNIEXPORT void JNICALL Java_dev_eatgrapes_live2d_CubismUserModel_setExpressionNative(JNIEnv* env, jclass, jlong ptr, jstring name) {
208+
const char* n = env->GetStringUTFChars(name, nullptr);
209+
((JniUserModel*)ptr)->setExpression(n);
210+
env->ReleaseStringUTFChars(name, n);
211+
}
212+
184213
JNIEXPORT void JNICALL Java_dev_eatgrapes_live2d_CubismUserModel_createRendererNative(JNIEnv*, jclass, jlong ptr) {
185214
((JniUserModel*)ptr)->CreateRenderer();
186215
}

0 commit comments

Comments
 (0)