-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
21 changed files
with
2,296 additions
and
1 deletion.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,107 @@ | ||
![Tilengine logo](JTilengine.png) | ||
# JTilengine | ||
Java binding for Tilengine (JNI bridge) | ||
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) | ||
## About JTilengine | ||
JTilengine is the Java binding for Tilengine. It is a direct 1:1 API translation of the original C library, so it is used and works exactly as its C counterpart.<br> | ||
The single difference with the C API, is that the original C API prepends every function calll with `TLN_` as a means of namespace, whereas in Java all the functions are housed as methods inside a single class called `Tilengine` without the `TLN_` prefix. | ||
> **Note:** this binding is not updated to the latest Tillengine release. It's stuck at release 1.11, but it's forwards compatible with current engine release. | ||
## Contents | ||
|
||
File(s) | Description | ||
----------------------|---------------------------------------------------------------- | ||
`Tilengine.java` | Java source with the API binding, required by any Java program | ||
`TilengineJNI.dll` | prebuilt required JNI binding binary for 32-bit windows | ||
`Tilengine.dll` | prebuilt required Tilengine binary for 32-bit windows | ||
`SDL2.dll` | prebuilt required SDL2 binary for 32-bit windows | ||
`TestWindow.java` | basic background test with raster effects, uses the built-in window | ||
`TestPanel.java` | basic background test with raster effects, uses the AWT JPanel | ||
`/assets` | graphic data used by the samples | ||
`/jni` | source of the JNI native library, ready to be built | ||
|
||
## Prerequisites | ||
1. JDK (Java Development Kit) must be properly installed and the `JAVA_HOME` environment variable set accordingly. Please visit https://www.oracle.com/index.html to know more about the JDK. | ||
2. Tilengine native shared library must be installed separately. Please refer to https://github.com/megamarc/Tilengine about how to do it.<br> | ||
For convenience, Windows 32-bit versions of Tilengine.dll and SDL2.dll are bundled.<br> | ||
|
||
## Documentation | ||
Work in progress of the C API documentation can be found here:<br> | ||
http://www.tilengine.org/doc/ | ||
|
||
## Installation | ||
No install step is required. Just make sure that the Tilengine library (*Tilengine.dll*, *Tilengine.java* and TilengineJNI.dll) are accessible from within your own project. | ||
|
||
## Building the JNI bridge | ||
Unlike other languages, Java requires an intermediate shared library to interface to any external native library. This package already ships with a prebuilt 32-bit windows. However, to use it on other platform or upgrade it with more up to date features, it must be built from source. The source is just a single .c source file. Any C compiler can be used, for example gcc or the excellent [tiny C compiler (tcc)](https://bellard.org/tcc/).<br> | ||
To build the source, open a console window inside the `jni` folder and type the following command depending on your target OS: | ||
|
||
### Windows | ||
``` | ||
gcc -shared -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" ..\Tilengine.dll TilengineJNI.c -o ..\TilengineJNI.dll | ||
``` | ||
|
||
### Linux (desktop, ARM raspberry...) | ||
``` | ||
gcc -shared -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\linux" -lTilengine TilengineJNI.c -o ..\libTilengineJNI.so | ||
``` | ||
|
||
### Mac OSX | ||
``` | ||
gcc -shared -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\darwin" Tilengine.dylib TilengineJNI.c -o ..\libTilengineJNI.jnilib | ||
``` | ||
|
||
## Basic program | ||
The following program does these actions: | ||
1. Create an instance of the main `Tilengine` class and name it `tln`. | ||
2. Initialize the engine with a resolution of 400x240, one layer, no sprites and 20 animation slots | ||
3. Set the loading path to the assets folder | ||
4. Load a *tilemap*, the asset that contains background layer data | ||
5. Attach the loaded tilemap to the allocated background layer | ||
6. Create a display window with default parameters: windowed, auto scale and CRT effect enabled | ||
7. Run the window loop, updating the display at each iteration until the window is closed | ||
|
||
Source code: | ||
```java | ||
public class Test { | ||
public static void main(String[] args) { | ||
Tilengine tln = new Tilengine (); | ||
tln.Init (400, 240, 1, 0, 20); | ||
tln.SetLoadPath ("assets"); | ||
|
||
int foreground = tln.LoadTilemap ("sonic_md_fg1.tmx", "Layer 1"); | ||
tln.SetLayer (0, 0, foreground); | ||
|
||
int frame = 0; | ||
tln.CreateWindow (null, 0); | ||
while (tln.ProcessWindow()){ | ||
tln.DrawFrame (frame); | ||
frame += 1; | ||
} | ||
tln.Deinit (); | ||
} | ||
} | ||
``` | ||
|
||
Resulting output: | ||
|
||
![Test](test.png) | ||
|
||
## Building and running the samples | ||
Two similar samples are provided to demonstrate the binding. Open a terminal console inside the main directory. | ||
|
||
### `TestWindow.java` | ||
This sample uses the built-in window (based on SDL2): | ||
``` | ||
javac TestWindow.java | ||
java TestWwindow | ||
``` | ||
|
||
### `TestPanel.java` | ||
This sample renders to an AWT JPanel, entirely discarding the built-in window: | ||
``` | ||
javac TestPanel.java | ||
java TestPanel | ||
``` | ||
|
||
## License | ||
JTilengine is released under the permissive MIT license |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
import javax.swing.JFrame; | ||
import javax.swing.JPanel; | ||
import java.awt.Dimension; | ||
import java.awt.Graphics; | ||
import java.awt.image.*; | ||
import java.awt.Image; | ||
import java.lang.System; | ||
|
||
public class TestPanel extends JPanel implements Runnable { | ||
|
||
Tilengine tln; | ||
int framew, frameh, framep; | ||
int panelw, panelh; | ||
Thread thread; | ||
int[] pixels; | ||
MemoryImageSource source; | ||
Image canvas; | ||
volatile boolean running; | ||
int frame = 0; | ||
int position = 0; | ||
int position_bg[] = new int[6]; | ||
int sky1[] = {0x1B, 0x00, 0x8B}; | ||
int sky2[] = {0x00, 0x74, 0xD7}; | ||
int sky3[] = {0x24, 0x92, 0xDB}; | ||
|
||
private static final int LAYER_FOREGROUND = 0; | ||
private static final int LAYER_BACKGROUND = 1; | ||
private static final int MAX_LAYER = 2; | ||
|
||
/* class constructor */ | ||
public TestPanel(int width, int height) { | ||
framew = width; | ||
frameh = height; | ||
framep = width*4; | ||
panelw = width*2; | ||
panelh = height*2; | ||
setPreferredSize (new Dimension(panelw,panelh)); | ||
thread = new Thread (this); | ||
thread.start (); | ||
} | ||
|
||
/* entry point */ | ||
public static void main(String[] args) { | ||
JFrame frame = new JFrame("Tilengine in JPanel example"); | ||
|
||
TestPanel panel = new TestPanel (400, 240); | ||
|
||
frame.add(panel); | ||
frame.pack(); | ||
frame.setVisible(true); | ||
frame.setResizable(false); | ||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); | ||
} | ||
|
||
/* main thread */ | ||
public void run () { | ||
|
||
/* resource holders */ | ||
int tilemaps[] = new int[MAX_LAYER]; | ||
int sequencepack; | ||
int seq_water; | ||
int palette; | ||
|
||
/* init tilengine */ | ||
tln = new Tilengine (); | ||
tln.Init (framew, frameh, 2,0,20); | ||
tln.SetRasterCallback (this, "rasterCallback"); | ||
|
||
/* load resources */ | ||
tln.SetLoadPath("assets"); | ||
tilemaps[LAYER_FOREGROUND] = tln.LoadTilemap ("Sonic_md_fg1.tmx", "Layer 1"); | ||
tilemaps[LAYER_BACKGROUND] = tln.LoadTilemap ("Sonic_md_bg1.tmx", "Layer 1"); | ||
sequencepack = tln.LoadSequencePack ("Sonic_md_seq.sqx"); | ||
seq_water = tln.FindSequence (sequencepack, "seq_water"); | ||
|
||
/* setup layers */ | ||
tln.SetLayer (LAYER_FOREGROUND, 0, tilemaps[LAYER_FOREGROUND]); | ||
tln.SetLayer (LAYER_BACKGROUND, 0, tilemaps[LAYER_BACKGROUND]); | ||
palette = tln.GetLayerPalette (LAYER_BACKGROUND); | ||
tln.SetPaletteAnimation (0, palette, seq_water, true); | ||
|
||
/* setup java framebuffer */ | ||
pixels = new int[framew*frameh]; | ||
source = new MemoryImageSource (framew, frameh, pixels, 0, framew); | ||
source.setAnimated (true); | ||
canvas = createImage (source); | ||
tln.SetRenderTarget (pixels, framep); | ||
|
||
/* main loop */ | ||
running = true; | ||
while (running) { | ||
update (); | ||
render (); | ||
|
||
try { | ||
Thread.sleep(10); | ||
} | ||
catch(InterruptedException ex){} | ||
frame++; | ||
} | ||
|
||
/* release resources */ | ||
tln.SetRenderTarget (null, 0); | ||
tln.DeleteTilemap (tilemaps[LAYER_FOREGROUND]); | ||
tln.DeleteTilemap (tilemaps[LAYER_BACKGROUND]); | ||
tln.DeleteSequencePack (sequencepack); | ||
tln.Deinit (); | ||
} | ||
|
||
/* update game logic */ | ||
void update () { | ||
position = (int)(frame * 3.0f); | ||
position_bg[0] = (int)(frame * 0.562f); | ||
position_bg[1] = (int)(frame * 0.437f); | ||
position_bg[2] = (int)(frame * 0.375f); | ||
position_bg[3] = (int)(frame * 0.625f); | ||
position_bg[4] = (int)(frame * 1.0f); | ||
position_bg[5] = (int)(frame * 2.0f); | ||
|
||
tln.SetLayerPosition (LAYER_FOREGROUND, position, 0); | ||
} | ||
|
||
/* update rendering */ | ||
void render () { | ||
try { | ||
tln.UpdateFrame (frame); | ||
Graphics g = this.getGraphics (); | ||
source.newPixels (0,0, framew,frameh); | ||
g.drawImage (canvas, 0,0, panelw, panelh, null); | ||
g.dispose (); | ||
} | ||
catch (Exception e) {} | ||
} | ||
|
||
/* virtual hblank interrupt (line callback) */ | ||
void rasterCallback (int line) { | ||
int pos = -1; | ||
|
||
/* background color */ | ||
if (line >= 0 && line <= 112) { | ||
int r = lerp (line, 0,112, sky1[0], sky2[0]); | ||
int g = lerp (line, 0,112, sky1[1], sky2[1]); | ||
int b = lerp (line, 0,112, sky1[2], sky2[2]); | ||
tln.SetBGColor (r,g,b); | ||
} | ||
else if (line == 152) | ||
tln.SetBGColor (sky3[0], sky3[1], sky3[2]); | ||
|
||
/* background layer strips */ | ||
if (line==0) | ||
pos = position_bg[0]; | ||
else if (line==32) | ||
pos = position_bg[1]; | ||
else if (line==48) | ||
pos = position_bg[2]; | ||
else if (line==64) | ||
pos = position_bg[3]; | ||
else if (line==112) | ||
pos = position_bg[4]; | ||
else if (line >= 152) | ||
pos = lerp (line, 152,224, position_bg[4], position_bg[5]); | ||
if (pos != -1) | ||
tln.SetLayerPosition (LAYER_BACKGROUND, pos, 0); | ||
} | ||
|
||
/* linear interpolation helper */ | ||
int lerp (int x, int x0, int x1, int fx0, int fx1) { | ||
return fx0 + (fx1 - fx0)*(x - x0)/(x1 - x0); | ||
} | ||
} |
Oops, something went wrong.