Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
megamarc committed Nov 15, 2018
1 parent 828e148 commit 6330b94
Show file tree
Hide file tree
Showing 21 changed files with 2,296 additions and 1 deletion.
Binary file added JTilengine.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
107 changes: 106 additions & 1 deletion README.md
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
Binary file added SDL2.dll
Binary file not shown.
170 changes: 170 additions & 0 deletions TestPanel.java
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);
}
}
Loading

0 comments on commit 6330b94

Please sign in to comment.