Skip to content

Commit fe33312

Browse files
committed
feat: expand native math kernels and interop docs
1 parent 886f16f commit fe33312

File tree

14 files changed

+479
-1
lines changed

14 files changed

+479
-1
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ Prereqs: JDK 17+, Gradle (wrapper included).
2020
./gradlew :editor:run
2121
```
2222

23+
## Docs
24+
25+
- VNS: `docs/VNS Scripting/VNS Scripting.md`
26+
- JES: `docs/JES Scripting/JES Scripting.md`
27+
- Interop: `docs/Interop.md`
28+
- Performance: `docs/Performance.md`
29+
2330
## Modules
2431

2532
- `core`: scene graph (`Scene2DBase`, `Entity2D`), components (`Panel2D`, `Label2D`, `Sprite2D`), physics (`RigidBody2D`, `PhysicsWorld2D`)

core/src/main/java/com/jvn/core/nativebridge/NativeMathBridge.java

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,40 @@ public static double[] matMul(double[] a, double[] b, int m, int n, int p) {
2626
}
2727
}
2828

29+
public static double[] matVec(double[] a, double[] x, int m, int n) {
30+
if (!LOADED) return matVecJava(a, x, m, n);
31+
try {
32+
return matVecNative(a, x, m, n);
33+
} catch (UnsatisfiedLinkError e) {
34+
return matVecJava(a, x, m, n);
35+
}
36+
}
37+
38+
public static double[] matMulBlocked(double[] a, double[] b, int m, int n, int p, int blockSize) {
39+
if (!LOADED) return matMulBlockedJava(a, b, m, n, p, blockSize);
40+
try {
41+
return matMulBlockedNative(a, b, m, n, p, blockSize);
42+
} catch (UnsatisfiedLinkError e) {
43+
return matMulBlockedJava(a, b, m, n, p, blockSize);
44+
}
45+
}
46+
47+
public static double[] conv2d(double[] input, int width, int height,
48+
double[] kernel, int kWidth, int kHeight) {
49+
if (!LOADED) return conv2dJava(input, width, height, kernel, kWidth, kHeight);
50+
try {
51+
return conv2dNative(input, width, height, kernel, kWidth, kHeight);
52+
} catch (UnsatisfiedLinkError e) {
53+
return conv2dJava(input, width, height, kernel, kWidth, kHeight);
54+
}
55+
}
56+
2957
private static native double dotProductNative(double[] a, double[] b);
3058
private static native double[] matMulNative(double[] a, double[] b, int m, int n, int p);
59+
private static native double[] matVecNative(double[] a, double[] x, int m, int n);
60+
private static native double[] matMulBlockedNative(double[] a, double[] b, int m, int n, int p, int blockSize);
61+
private static native double[] conv2dNative(double[] input, int width, int height,
62+
double[] kernel, int kWidth, int kHeight);
3163

3264
private static double dotProductJava(double[] a, double[] b) {
3365
if (a == null || b == null) return 0.0;
@@ -56,4 +88,77 @@ private static double[] matMulJava(double[] a, double[] b, int m, int n, int p)
5688
}
5789
return out;
5890
}
91+
92+
private static double[] matVecJava(double[] a, double[] x, int m, int n) {
93+
if (a == null || x == null || m <= 0 || n <= 0) return new double[0];
94+
if (a.length < m * n || x.length < n) return new double[0];
95+
double[] out = new double[m];
96+
for (int i = 0; i < m; i++) {
97+
int aRow = i * n;
98+
double sum = 0.0;
99+
for (int k = 0; k < n; k++) {
100+
sum += a[aRow + k] * x[k];
101+
}
102+
out[i] = sum;
103+
}
104+
return out;
105+
}
106+
107+
private static double[] matMulBlockedJava(double[] a, double[] b, int m, int n, int p, int blockSize) {
108+
if (a == null || b == null || m <= 0 || n <= 0 || p <= 0) return new double[0];
109+
if (a.length < m * n || b.length < n * p) return new double[0];
110+
int bs = blockSize <= 0 ? 32 : blockSize;
111+
double[] out = new double[m * p];
112+
for (int ii = 0; ii < m; ii += bs) {
113+
int iMax = Math.min(m, ii + bs);
114+
for (int kk = 0; kk < n; kk += bs) {
115+
int kMax = Math.min(n, kk + bs);
116+
for (int jj = 0; jj < p; jj += bs) {
117+
int jMax = Math.min(p, jj + bs);
118+
for (int i = ii; i < iMax; i++) {
119+
int aRow = i * n;
120+
int cRow = i * p;
121+
for (int k = kk; k < kMax; k++) {
122+
double aval = a[aRow + k];
123+
int bRow = k * p;
124+
for (int j = jj; j < jMax; j++) {
125+
out[cRow + j] += aval * b[bRow + j];
126+
}
127+
}
128+
}
129+
}
130+
}
131+
}
132+
return out;
133+
}
134+
135+
private static double[] conv2dJava(double[] input, int width, int height,
136+
double[] kernel, int kWidth, int kHeight) {
137+
if (input == null || kernel == null || width <= 0 || height <= 0 || kWidth <= 0 || kHeight <= 0) {
138+
return new double[0];
139+
}
140+
if (input.length < width * height || kernel.length < kWidth * kHeight) return new double[0];
141+
double[] out = new double[width * height];
142+
int kCenterX = kWidth / 2;
143+
int kCenterY = kHeight / 2;
144+
for (int y = 0; y < height; y++) {
145+
int row = y * width;
146+
for (int x = 0; x < width; x++) {
147+
double sum = 0.0;
148+
for (int ky = 0; ky < kHeight; ky++) {
149+
int iy = y + ky - kCenterY;
150+
if (iy < 0 || iy >= height) continue;
151+
int iRow = iy * width;
152+
int kRow = ky * kWidth;
153+
for (int kx = 0; kx < kWidth; kx++) {
154+
int ix = x + kx - kCenterX;
155+
if (ix < 0 || ix >= width) continue;
156+
sum += input[iRow + ix] * kernel[kRow + kx];
157+
}
158+
}
159+
out[row + x] = sum;
160+
}
161+
}
162+
return out;
163+
}
59164
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package com.jvn.core.nativebridge;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
6+
import static org.junit.jupiter.api.Assertions.assertEquals;
7+
8+
public class NativeMathBridgeTest {
9+
@Test
10+
void dotProductMatches() {
11+
double[] a = {1, 2, 3};
12+
double[] b = {4, 5, 6};
13+
double res = NativeMathBridge.dotProduct(a, b);
14+
assertEquals(32.0, res, 1e-9);
15+
}
16+
17+
@Test
18+
void matMulMatches() {
19+
double[] a = {
20+
1, 2, 3,
21+
4, 5, 6
22+
};
23+
double[] b = {
24+
7, 8,
25+
9, 10,
26+
11, 12
27+
};
28+
double[] out = NativeMathBridge.matMul(a, b, 2, 3, 2);
29+
assertArrayEquals(new double[] {58, 64, 139, 154}, out, 1e-9);
30+
}
31+
32+
@Test
33+
void matVecMatches() {
34+
double[] a = {
35+
1, 2, 3,
36+
4, 5, 6
37+
};
38+
double[] x = {2, 3, 4};
39+
double[] out = NativeMathBridge.matVec(a, x, 2, 3);
40+
assertArrayEquals(new double[] {20, 47}, out, 1e-9);
41+
}
42+
43+
@Test
44+
void matMulBlockedMatches() {
45+
double[] a = {
46+
1, 2, 3,
47+
4, 5, 6
48+
};
49+
double[] b = {
50+
7, 8,
51+
9, 10,
52+
11, 12
53+
};
54+
double[] out = NativeMathBridge.matMulBlocked(a, b, 2, 3, 2, 2);
55+
assertArrayEquals(new double[] {58, 64, 139, 154}, out, 1e-9);
56+
}
57+
58+
@Test
59+
void conv2dMatches() {
60+
double[] input = {
61+
1, 2, 1,
62+
0, 1, 0,
63+
2, 1, 2
64+
};
65+
double[] kernel = {
66+
0, 1, 0,
67+
1, -4, 1,
68+
0, 1, 0
69+
};
70+
double[] out = NativeMathBridge.conv2d(input, 3, 3, kernel, 3, 3);
71+
assertArrayEquals(new double[] {
72+
-2, -5, -2,
73+
4, -1, 4,
74+
-7, 1, -7
75+
}, out, 1e-9);
76+
}
77+
}

docs/Interop.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Interop Guide
2+
3+
This document describes how VNS, JES, and runtime code talk to each other.
4+
5+
## VNS → Runtime
6+
7+
VNS `[command ...]` lines are parsed into `VnExternalCommand` entries. The runtime handles them through `VnInterop`:
8+
9+
- `core/src/main/java/com/jvn/core/vn/DefaultVnInterop.java`
10+
- `runtime/src/main/java/com/jvn/runtime/RuntimeVnInterop.java`
11+
12+
### Default providers (engine-wide)
13+
14+
- `hud <msg>` → temporary HUD toast.
15+
- `java <Class#method args...>` → invoke a public static Java method.
16+
- `var set|inc|dec|flag|unflag|clear ...` → set VN variables.
17+
- `cond if <expr>` → conditional jump logic (used by choices).
18+
- `settings` / `mode` / `ui` / `history` → player settings & overlays.
19+
- `audio pause|resume|seek|crossfade ...`
20+
- `screen shake|flash ...`
21+
- `save` (quick save/load helpers).
22+
23+
### Runtime providers
24+
25+
- `jes push|replace|pop|call ...`
26+
- `vns push|replace|goto ...`
27+
- `menu settings|save|load|main ...`
28+
29+
These are implemented by `RuntimeVnInterop` and only available in the runtime module.
30+
31+
## VNS ↔ JES
32+
33+
From VNS:
34+
35+
- `[jes push <script.jes> label <returnLabel> with k=v ...]`
36+
- `[jes replace <script.jes> ...]`
37+
- `[jes pop]`
38+
- `[jes call <name> k=v ...]`
39+
40+
From JES:
41+
42+
- `call "return" { label: "after_game", score: 123 }`
43+
- Pops the JES scene.
44+
- Copies props into VN variables (except `label`/`goto`).
45+
- Jumps to the return label (prop wins, else VNS `label` argument).
46+
- `call "vns"` is an alias of `return`.
47+
- `call "hud" { msg: "Saved!" }` shows a VN toast.
48+
- `call "pop" {}` pops without jumping.
49+
50+
## JES Call Handlers
51+
52+
If you load JES from Java, you can attach handlers:
53+
54+
```java
55+
JesScene2D scene = JesLoader.load(in);
56+
scene.registerCall("spawnWave", props -> { /* ... */ });
57+
scene.setActionHandler((name, props) -> { /* fallback */ });
58+
```
59+
60+
Handlers are invoked for `call "name" { ... }` statements or trigger volumes.
61+
62+
## Data Types
63+
64+
Interop props are parsed as:
65+
- `true/false` → boolean
66+
- numbers with `.` → double, integers otherwise
67+
- everything else → string
68+
69+
Avoid spaces in values (`difficulty=hard`, `title=Hello_World`).

docs/JES Scripting/JES Scripting.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ JES (JVN Engine Script) is a small DSL for scenes, entities, components, input,
2222
```jes
2323
call "return" { label: "after_game" score: 123 }
2424
```
25-
- See also: `docs/VNS Scripting.md` (calling JES) and `docs/Timeline Scripting.md` (narrative flow).
25+
- See also: `docs/VNS Scripting/VNS Scripting.md` (calling JES), `docs/Timeline Scripting/Timeline Scripting.md` (narrative flow), and `docs/Interop.md`.
2626

2727
## Example
2828

@@ -106,6 +106,27 @@ call "return" { label: "after_game" score: 12345 }
106106

107107
See also: docs/VNS Scripting.md → "JES interop from VNS".
108108

109+
## Runtime hooks (Java)
110+
111+
If you load JES from Java, you can register handlers to respond to `call` statements, input events, or triggers.
112+
113+
```java
114+
JesScene2D scene = JesLoader.load(in);
115+
116+
scene.registerCall("spawnWave", props -> {
117+
// Use props like { count: 5, speed: 120 }
118+
});
119+
120+
scene.setActionHandler((name, props) -> {
121+
// Fallback for any call not explicitly registered
122+
});
123+
```
124+
125+
Notes:
126+
- `registerCall` attaches specific handlers by name.
127+
- `setActionHandler` is a catch-all callback used if no handler matches.
128+
- Built-in `call` names like `warpMap` or `attack` are handled internally before handlers run.
129+
109130
## Easing Types
110131
Optional easing property for timeline actions:
111132
- linear (default)

docs/Overview.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,10 @@ Key highlights:
1313
- Lightweight, pure-Java, JDK 17
1414
- Swing and JavaFX rendering backends
1515
- Extendable loader and components
16+
17+
## Docs
18+
19+
- `docs/VNS Scripting/VNS Scripting.md` - visual novel scripting
20+
- `docs/JES Scripting/JES Scripting.md` - scene DSL and runtime hooks
21+
- `docs/Interop.md` - VNS/JES/runtime interoperability
22+
- `docs/Performance.md` - lightweight build tips

docs/Performance.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Lightweight Builds & Performance
2+
3+
These tips help keep JVN shipped games small and fast.
4+
Quickly whipped up doc, I'll add more to it later.
5+
6+
## Assets
7+
- Favor smaller textures; target your runtime resolution to avoid scaling oversized art.
8+
- Use spritesheets to reduce file count and texture binds.
9+
- Prefer compressed audio (OGG/MP3) for BGM/SFX; avoid uncompressed WAV for long tracks.
10+
- Keep optional high-res assets in external packs and load via `--assets`.
11+
12+
## VNS
13+
- Reuse backgrounds and character sprites across scenes.
14+
- Limit heavy transitions in rapid succession.
15+
- Avoid excessive HUD messages or screen effects during fast input sequences.
16+
17+
## JES / Scene2D
18+
- Keep entity counts low per scene; reuse pooled entities where possible.
19+
- Minimize active physics bodies and sensors.
20+
- Disable debug overlays in production.
21+
- Prefer simple timeline actions over per-frame scripted logic.
22+
23+
## Runtime
24+
- Use `ViewportScaler2D.fit` to render at a stable base resolution.
25+
- Cache assets using `AssetCatalog` and avoid reloading each frame.
26+
- Use the JavaFX backend only when needed; Swing remains a lighter option.
27+
28+
## Native Bridge (Optional)
29+
- Native math kernels are optional. If the native library is missing, the Java fallback runs.
30+
- Bundle native libs only when a measurable win is needed on target hardware.

0 commit comments

Comments
 (0)