11package dev .eatgrapes .live2d .example ;
22
3+ import com .google .gson .Gson ;
4+ import com .google .gson .JsonArray ;
5+ import com .google .gson .JsonElement ;
6+ import com .google .gson .JsonObject ;
37import com .sun .net .httpserver .HttpServer ;
48import dev .eatgrapes .live2d .CubismFramework ;
59import dev .eatgrapes .live2d .CubismUserModel ;
812import org .lwjgl .system .MemoryStack ;
913
1014import java .io .InputStream ;
15+ import java .io .InputStreamReader ;
16+ import java .io .Reader ;
1117import java .net .InetSocketAddress ;
1218import java .nio .ByteBuffer ;
13- import java .nio .DoubleBuffer ;
1419import java .nio .IntBuffer ;
20+ import java .nio .charset .StandardCharsets ;
1521import java .nio .file .Files ;
1622import java .nio .file .Path ;
1723import java .nio .file .StandardCopyOption ;
24+ import java .util .ArrayList ;
1825import java .util .HashMap ;
26+ import java .util .List ;
1927import java .util .Map ;
2028import java .util .concurrent .ConcurrentLinkedQueue ;
2129
2634public class Main {
2735 private long window ;
2836 private CubismUserModel model ;
29- private final Map <String , byte []> motions = new HashMap <>();
37+ private final Map <String , List <byte []>> motionGroups = new HashMap <>();
38+ private final List <Integer > loadedTextures = new ArrayList <>();
3039 private final float [] mvp = new float []{1 ,0 ,0 ,0 , 0 ,1 ,0 ,0 , 0 ,0 ,1 ,0 , 0 ,0 ,0 ,1 };
3140 private final ConcurrentLinkedQueue <Runnable > taskQueue = new ConcurrentLinkedQueue <>();
3241 private float modelScale = 1.0f ;
@@ -46,8 +55,20 @@ private void startServer() throws Exception {
4655 String query = t .getRequestURI ().getQuery ();
4756 String id = query .split ("=" )[1 ];
4857 taskQueue .add (() -> {
49- if (motions .containsKey (id )) {
50- model .startMotion (motions .get (id ), 3 , false , null );
58+ String key = id .toLowerCase ();
59+ if (key .equals ("tap_body" )) key = "tapbody" ;
60+
61+ List <byte []> group = null ;
62+ for (String k : motionGroups .keySet ()) {
63+ if (k .equalsIgnoreCase (key )) {
64+ group = motionGroups .get (k );
65+ break ;
66+ }
67+ }
68+
69+ if (group != null && !group .isEmpty ()) {
70+ int idx = (int ) (Math .random () * group .size ());
71+ model .startMotion (group .get (idx ), 3 , false , null );
5172 }
5273 });
5374 t .sendResponseHeaders (200 , 0 );
@@ -84,6 +105,19 @@ private void startServer() throws Exception {
84105 t .sendResponseHeaders (200 , 0 );
85106 t .close ();
86107 });
108+ server .createContext ("/model" , t -> {
109+ String query = t .getRequestURI ().getQuery ();
110+ String name = query .split ("=" )[1 ];
111+ taskQueue .add (() -> {
112+ try {
113+ loadModel (name );
114+ } catch (Exception e ) {
115+ e .printStackTrace ();
116+ }
117+ });
118+ t .sendResponseHeaders (200 , 0 );
119+ t .close ();
120+ });
87121 server .setExecutor (null );
88122 server .start ();
89123 System .out .println ("Control server started on port 8080" );
@@ -108,20 +142,77 @@ private void init() {
108142 if (model != null ) model .setDragging (nx , ny );
109143 }
110144 });
145+
146+ CubismFramework .startUp ();
147+ CubismFramework .initialize ();
111148 }
112149
113150 private void setup () throws Exception {
114- CubismFramework .startUp ();
115- CubismFramework .initialize ();
151+ loadModel ("Hiyori" );
152+ }
153+
154+ private void loadModel (String name ) throws Exception {
155+ if (model != null ) {
156+ model .close ();
157+ model = null ;
158+ }
159+ for (int tex : loadedTextures ) {
160+ glDeleteTextures (tex );
161+ }
162+ loadedTextures .clear ();
163+ motionGroups .clear ();
164+
165+ String baseDir = "/model/" + name + "/" ;
166+ String model3Path = baseDir + name + ".model3.json" ;
167+
168+ Gson gson = new Gson ();
169+ JsonObject settings ;
170+ try (InputStream is = getClass ().getResourceAsStream (model3Path )) {
171+ if (is == null ) throw new RuntimeException ("Model config not found: " + model3Path );
172+ try (Reader reader = new InputStreamReader (is , StandardCharsets .UTF_8 )) {
173+ settings = gson .fromJson (reader , JsonObject .class );
174+ }
175+ }
176+
116177 model = new CubismUserModel ();
117- model .loadModel (load ("/model/Hiyori/Hiyori.moc3" ));
118- model .loadPose (load ("/model/Hiyori/Hiyori.pose3.json" ));
119- model .loadPhysics (load ("/model/Hiyori/Hiyori.physics3.json" ));
178+
179+ JsonObject refs = settings .getAsJsonObject ("FileReferences" );
180+
181+ String mocFile = refs .get ("Moc" ).getAsString ();
182+ model .loadModel (load (baseDir + mocFile ));
183+
184+ if (refs .has ("Pose" )) {
185+ model .loadPose (load (baseDir + refs .get ("Pose" ).getAsString ()));
186+ }
187+
188+ if (refs .has ("Physics" )) {
189+ model .loadPhysics (load (baseDir + refs .get ("Physics" ).getAsString ()));
190+ }
191+
120192 model .createRenderer ();
121- model .registerTexture (0 , loadTex ("/model/Hiyori/Hiyori.2048/texture_00.png" ));
122- model .registerTexture (1 , loadTex ("/model/Hiyori/Hiyori.2048/texture_01.png" ));
123- motions .put ("idle" , load ("/model/Hiyori/motions/Hiyori_m01.motion3.json" ));
124- motions .put ("tap_body" , load ("/model/Hiyori/motions/Hiyori_m04.motion3.json" ));
193+
194+ JsonArray textures = refs .getAsJsonArray ("Textures" );
195+ for (int i = 0 ; i < textures .size (); i ++) {
196+ String texFile = textures .get (i ).getAsString ();
197+ model .registerTexture (i , loadTex (baseDir + texFile ));
198+ }
199+
200+ if (refs .has ("Motions" )) {
201+ JsonObject motionsObj = refs .getAsJsonObject ("Motions" );
202+ for (String groupName : motionsObj .keySet ()) {
203+ JsonArray groupArr = motionsObj .getAsJsonArray (groupName );
204+ List <byte []> loadedGroup = new ArrayList <>();
205+ for (JsonElement elem : groupArr ) {
206+ JsonObject m = elem .getAsJsonObject ();
207+ String mFile = m .get ("File" ).getAsString ();
208+ try {
209+ loadedGroup .add (load (baseDir + mFile ));
210+ } catch (Exception e ) {
211+ }
212+ }
213+ motionGroups .put (groupName , loadedGroup );
214+ }
215+ }
125216 }
126217
127218 private void loop () {
@@ -138,31 +229,40 @@ private void loop() {
138229 for (int i = 0 ; i < 16 ; i ++) mvp [i ] = 0 ;
139230 mvp [0 ] = modelScale / aspect ; mvp [5 ] = modelScale ; mvp [10 ] = 1.0f ; mvp [15 ] = 1.0f ;
140231 }
141- model .update (0.016f );
142- model .draw (mvp );
232+ if (model != null ) {
233+ model .update (0.016f );
234+ model .draw (mvp );
235+ }
143236 glfwSwapBuffers (window );
144237 glfwPollEvents ();
145238 }
146239 }
147240
148241 private void cleanup () {
149- model .close ();
242+ if ( model != null ) model .close ();
150243 CubismFramework .dispose ();
151244 glfwTerminate ();
152245 }
153246
154247 private byte [] load (String p ) throws Exception {
155- try (InputStream is = getClass ().getResourceAsStream (p )) { return is .readAllBytes (); }
248+ try (InputStream is = getClass ().getResourceAsStream (p )) {
249+ if (is == null ) throw new RuntimeException ("Resource not found: " + p );
250+ return is .readAllBytes ();
251+ }
156252 }
157253
158254 private int loadTex (String p ) throws Exception {
159255 Path tmp = Files .createTempFile ("l2d" , ".png" );
160- try (InputStream is = getClass ().getResourceAsStream (p )) { Files .copy (is , tmp , StandardCopyOption .REPLACE_EXISTING ); }
256+ try (InputStream is = getClass ().getResourceAsStream (p )) {
257+ if (is == null ) throw new RuntimeException ("Texture not found: " + p );
258+ Files .copy (is , tmp , StandardCopyOption .REPLACE_EXISTING );
259+ }
161260 int w , h , tex ;
162261 ByteBuffer img ;
163262 try (MemoryStack s = MemoryStack .stackPush ()) {
164263 IntBuffer wb = s .mallocInt (1 ), hb = s .mallocInt (1 ), cb = s .mallocInt (1 );
165264 img = STBImage .stbi_load (tmp .toString (), wb , hb , cb , 4 );
265+ if (img == null ) throw new RuntimeException ("Failed to load texture image: " + p );
166266 w = wb .get (); h = hb .get ();
167267 }
168268 tex = glGenTextures ();
@@ -174,6 +274,7 @@ private int loadTex(String p) throws Exception {
174274 glTexImage2D (GL_TEXTURE_2D , 0 , GL_RGBA , w , h , 0 , GL_RGBA , GL_UNSIGNED_BYTE , img );
175275 STBImage .stbi_image_free (img );
176276 Files .delete (tmp );
277+ loadedTextures .add (tex );
177278 return tex ;
178279 }
179280
0 commit comments