Skip to content

Commit 6291b5d

Browse files
committed
Initial commit
0 parents  commit 6291b5d

34 files changed

Lines changed: 15924 additions & 0 deletions

.github/workflows/build.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: Build All Architectures
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
build:
7+
name: Build on ${{ matrix.os }}
8+
runs-on: ${{ matrix.os }}
9+
strategy:
10+
fail-fast: false
11+
matrix:
12+
os: [ubuntu-latest, windows-latest, macos-latest]
13+
14+
steps:
15+
- uses: actions/checkout@v4
16+
with:
17+
submodules: recursive
18+
19+
- name: Set up JDK 21
20+
uses: actions/setup-java@v4
21+
with:
22+
java-version: '21'
23+
distribution: 'temurin'
24+
25+
- name: Set up Python
26+
uses: actions/setup-python@v5
27+
with:
28+
python-version: '3.x'
29+
30+
- name: Install dependencies (Linux)
31+
if: runner.os == 'Linux'
32+
run: sudo apt-get update && sudo apt-get install -y libgles2-mesa-dev
33+
34+
- name: Build and Package
35+
run: python3 scripts/build.py
36+
37+
- name: Upload Artifacts
38+
uses: actions/upload-artifact@v4
39+
with:
40+
name: live2d-jars-${{ runner.os }}
41+
path: out/*.jar

.gitignore

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Build outputs
2+
out/
3+
native/build/
4+
*.jar
5+
target/
6+
**/target/
7+
*.log
8+
9+
# SDK
10+
/sdk/
11+
12+
# IDE
13+
.idea/
14+
*.iml
15+
.vscode/
16+
17+
# Python
18+
__pycache__/

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Eatgrapes
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Live2D-JavaBinding
2+
3+
A Native Java binding for the Live2D Cubism SDK.
4+
5+
> **Status: Work in Progress**
6+
> This project is currently under development. Only basic model display and idle motion updates are implemented.
7+
8+
## Features
9+
10+
- **Distributed Separately**: Java logic and native binaries are decoupled. Native JARs are self-contained (statically linked with Cubism Core).
11+
- **Cross-Platform**: Supports Windows (x64), Linux (x64, arm64), and macOS (x64, arm64).
12+
- **Simple API**: Easy-to-use Apis to rendering.
13+
14+
## Quick Usage example
15+
16+
```java
17+
import dev.eatgrapes.live2d.CubismFramework;
18+
import dev.eatgrapes.live2d.CubismUserModel;
19+
20+
// 1. Initialize the Framework
21+
CubismFramework.startUp();
22+
CubismFramework.initialize();
23+
24+
// 2. Load Model & Components
25+
CubismUserModel model = new CubismUserModel();
26+
model.loadModel(moc3Bytes);
27+
model.loadPose(poseBytes);
28+
model.loadPhysics(physicsBytes);
29+
30+
// 3. Setup Renderer
31+
model.createRenderer();
32+
model.registerTexture(0, openGLTextureId);
33+
34+
// 4. Update & Draw (in your render loop)
35+
model.update(deltaTime);
36+
model.draw(mvpMatrix);
37+
```
38+
39+
## Project Structure
40+
41+
- `binding/`: Java API and module definitions.
42+
- `native/`: JNI implementation and CMake build scripts.
43+
- `scripts/`: Python build automation for all platforms.
44+
- `example/`: A complete Maven-based example using LWJGL 3.
45+
46+
## Building
47+
48+
Requires Python 3, CMake, a C++14 compiler, and JDK 9+.
49+
50+
```bash
51+
python3 scripts/build.py
52+
```
53+
54+
The artifacts will be generated in the `out/` directory:
55+
- `live2d-shared.jar` (Java API)
56+
- `live2d-native-[platform].jar` (Self-contained Native Library)
57+
58+
## Contributing
59+
60+
Pull Requests (PRs) are welcome! If you'd like to help implement motion managers, expression support, or other features, feel free to contribute.
61+
62+
## License
63+
64+
- **Binding Code**: Distributed under the [MIT License](LICENSE).
65+
- **Live2D Cubism SDK**: The use of the underlying SDK is governed by the [Live2D Open Software License Agreement](https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html). You must agree to their terms to use the native components.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package dev.eatgrapes.live2d;
2+
3+
public class CubismFramework {
4+
static {
5+
LibraryLoader.load();
6+
}
7+
8+
public static native void startUp();
9+
public static native void initialize();
10+
public static native void dispose();
11+
public static native boolean isStarted();
12+
public static native boolean isInitialized();
13+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package dev.eatgrapes.live2d;
2+
3+
public class CubismUserModel extends Native {
4+
5+
public CubismUserModel() {
6+
super(createNative());
7+
}
8+
9+
private static native long createNative();
10+
11+
public void loadModel(byte[] buffer) {
12+
loadModelNative(_ptr, buffer);
13+
}
14+
15+
private static native void loadModelNative(long ptr, byte[] buffer);
16+
17+
public void loadPhysics(byte[] buffer) {
18+
loadPhysicsNative(_ptr, buffer);
19+
}
20+
21+
private static native void loadPhysicsNative(long ptr, byte[] buffer);
22+
23+
public void loadPose(byte[] buffer) {
24+
loadPoseNative(_ptr, buffer);
25+
}
26+
27+
private static native void loadPoseNative(long ptr, byte[] buffer);
28+
29+
public void loadExpression(byte[] buffer, String name) {
30+
loadExpressionNative(_ptr, buffer, name);
31+
}
32+
33+
private static native void loadExpressionNative(long ptr, byte[] buffer, String name);
34+
35+
public void createRenderer() {
36+
createRendererNative(_ptr);
37+
}
38+
39+
private static native void createRendererNative(long ptr);
40+
41+
public void update(float deltaTime) {
42+
updateNative(_ptr, deltaTime);
43+
}
44+
45+
private static native void updateNative(long ptr, float deltaTime);
46+
47+
public void registerTexture(int index, int textureId) {
48+
registerTextureNative(_ptr, index, textureId);
49+
}
50+
51+
private static native void registerTextureNative(long ptr, int index, int textureId);
52+
53+
public void setParameterValue(String id, float value) {
54+
setParameterValueNative(_ptr, id, value);
55+
}
56+
57+
private static native void setParameterValueNative(long ptr, String id, float value);
58+
59+
public float getCanvasWidth() {
60+
return getCanvasWidthNative(_ptr);
61+
}
62+
63+
private static native float getCanvasWidthNative(long ptr);
64+
65+
public float getCanvasHeight() {
66+
return getCanvasHeightNative(_ptr);
67+
}
68+
69+
private static native float getCanvasHeightNative(long ptr);
70+
71+
public void draw(float[] mvpMatrix) {
72+
drawNative(_ptr, mvpMatrix);
73+
}
74+
75+
private static native void drawNative(long ptr, float[] mvpMatrix);
76+
77+
@Override
78+
public void close() {
79+
deleteNative(_ptr);
80+
}
81+
82+
private static native void deleteNative(long ptr);
83+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package dev.eatgrapes.live2d;
2+
3+
import java.io.*;
4+
import java.nio.file.*;
5+
import java.util.Locale;
6+
7+
public class LibraryLoader {
8+
private static boolean loaded = false;
9+
10+
public static synchronized void load() {
11+
if (loaded) return;
12+
13+
String osName = System.getProperty("os.name").toLowerCase(Locale.ROOT);
14+
String arch = System.getProperty("os.arch").toLowerCase(Locale.ROOT);
15+
16+
String os;
17+
if (osName.contains("win")) os = "windows";
18+
else if (osName.contains("mac")) os = "macos";
19+
else if (osName.contains("linux")) os = "linux";
20+
else throw new RuntimeException("Unsupported OS: " + osName);
21+
22+
String platformArch;
23+
if (arch.contains("aarch64") || arch.contains("arm64")) platformArch = "arm64";
24+
else if (arch.contains("64")) platformArch = "x64";
25+
else throw new RuntimeException("Unsupported arch: " + arch);
26+
27+
String platformTag = os + "-" + platformArch;
28+
String libName = System.mapLibraryName("live2d_jni");
29+
String resourcePath = "/" + platformTag + "/" + libName;
30+
31+
try (InputStream is = LibraryLoader.class.getResourceAsStream(resourcePath)) {
32+
if (is == null) throw new RuntimeException("Native lib not found: " + resourcePath);
33+
34+
Path tempDir = Files.createTempDirectory("live2d_native");
35+
File tempFile = tempDir.resolve(libName).toFile();
36+
tempFile.deleteOnExit();
37+
38+
Files.copy(is, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
39+
System.load(tempFile.getAbsolutePath());
40+
loaded = true;
41+
} catch (IOException e) {
42+
throw new RuntimeException("Failed to extract native lib", e);
43+
}
44+
}
45+
46+
public static byte[] loadResource(String name) {
47+
String internalPath = "/live2d/shaders/" + name.substring(name.lastIndexOf("/") + 1);
48+
try (InputStream is = LibraryLoader.class.getResourceAsStream(internalPath)) {
49+
if (is == null) return null;
50+
return is.readAllBytes();
51+
} catch (IOException e) {
52+
return null;
53+
}
54+
}
55+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package dev.eatgrapes.live2d;
2+
3+
public abstract class Native implements AutoCloseable {
4+
protected final long _ptr;
5+
6+
protected Native(long ptr) {
7+
if (ptr == 0) throw new RuntimeException("Native pointer is null");
8+
this._ptr = ptr;
9+
}
10+
11+
public long getPtr() {
12+
return _ptr;
13+
}
14+
15+
@Override
16+
public abstract void close();
17+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module dev.eatgrapes.live2d {
2+
exports dev.eatgrapes.live2d;
3+
}

example/pom.xml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<groupId>dev.eatgrapes</groupId>
8+
<artifactId>live2d-example</artifactId>
9+
<version>1.0-SNAPSHOT</version>
10+
11+
<properties>
12+
<maven.compiler.source>21</maven.compiler.source>
13+
<maven.compiler.target>21</maven.compiler.target>
14+
<lwjgl.version>3.3.4</lwjgl.version>
15+
<lwjgl.natives>natives-linux-arm64</lwjgl.natives>
16+
</properties>
17+
18+
<dependencyManagement>
19+
<dependencies>
20+
<dependency>
21+
<groupId>org.lwjgl</groupId>
22+
<artifactId>lwjgl-bom</artifactId>
23+
<version>${lwjgl.version}</version>
24+
<scope>import</scope>
25+
<type>pom</type>
26+
</dependency>
27+
</dependencies>
28+
</dependencyManagement>
29+
30+
<dependencies>
31+
<dependency>
32+
<groupId>dev.eatgrapes</groupId>
33+
<artifactId>live2d-shared</artifactId>
34+
<version>1.0-SNAPSHOT</version>
35+
</dependency>
36+
<dependency>
37+
<groupId>dev.eatgrapes</groupId>
38+
<artifactId>live2d-native</artifactId>
39+
<version>1.0-SNAPSHOT</version>
40+
<classifier>linux-arm64</classifier>
41+
</dependency>
42+
<dependency><groupId>org.lwjgl</groupId><artifactId>lwjgl</artifactId></dependency>
43+
<dependency><groupId>org.lwjgl</groupId><artifactId>lwjgl-glfw</artifactId></dependency>
44+
<dependency><groupId>org.lwjgl</groupId><artifactId>lwjgl-opengl</artifactId></dependency>
45+
<dependency><groupId>org.lwjgl</groupId><artifactId>lwjgl-stb</artifactId></dependency>
46+
<dependency><groupId>org.lwjgl</groupId><artifactId>lwjgl</artifactId><classifier>${lwjgl.natives}</classifier></dependency>
47+
<dependency><groupId>org.lwjgl</groupId><artifactId>lwjgl-glfw</artifactId><classifier>${lwjgl.natives}</classifier></dependency>
48+
<dependency><groupId>org.lwjgl</groupId><artifactId>lwjgl-opengl</artifactId><classifier>${lwjgl.natives}</classifier></dependency>
49+
<dependency><groupId>org.lwjgl</groupId><artifactId>lwjgl-stb</artifactId><classifier>${lwjgl.natives}</classifier></dependency>
50+
</dependencies>
51+
52+
<build>
53+
<plugins>
54+
<plugin>
55+
<groupId>org.codehaus.mojo</groupId>
56+
<artifactId>exec-maven-plugin</artifactId>
57+
<version>3.1.0</version>
58+
<configuration>
59+
<mainClass>dev.eatgrapes.live2d.example.Main</mainClass>
60+
</configuration>
61+
</plugin>
62+
</plugins>
63+
</build>
64+
</project>

0 commit comments

Comments
 (0)