mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-16 08:36:07 +09:00
wip
This commit is contained in:
206
src/net/torvald/terrarum/worlddrawer/FireRecalculateEvent.c
Normal file
206
src/net/torvald/terrarum/worlddrawer/FireRecalculateEvent.c
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
#include <xmmintrin.h>
|
||||||
|
#include <jni.h>
|
||||||
|
#include "net_torvald_terrarum_worlddrawer_LightmapRenderer.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
compile required headers with:
|
||||||
|
"C:\Program Files\Java\jdk1.8.0_131\bin\javah.exe" -jni -cp "./lib/gdx.jar;C:\Users\minjaesong\.IdeaIC2018.3\config\plugins\JBSDKDownloadHelper\lib\kotlin-runtime.jar;C:\Users\minjaesong\Documents\terr
|
||||||
|
arum\build\libs\Terrarum-0.2.jar" net.torvald.terrarum.worlddrawer.LightmapRenderer
|
||||||
|
|
||||||
|
get method signatures with:
|
||||||
|
"C:\Program Files\Java\jdk1.8.0_131\bin\javap.exe" -s -p -cp "./lib/gdx.jar;C:\Users\minjaesong\.IdeaIC2018.3\config\plugins\JBSDKDownloadHelper\lib\kotlin-runtime.jar;C:\Users\minjaesong\Documents\ter
|
||||||
|
rarum\build\libs\Terrarum-0.2.jar" net.torvald.terrarum.gameworld.GameWorld
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define vec4 __m128
|
||||||
|
#define overscan_open net_torvald_terrarum_worlddrawer_LightmapRenderer_overscan_open
|
||||||
|
#define DIV_FLOAT net_torvald_terrarum_worlddrawer_LightmapRenderer_DIV_FLOAT
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int type;
|
||||||
|
float fill;
|
||||||
|
} FluidInfo;
|
||||||
|
|
||||||
|
vec4 lightLevelThis;
|
||||||
|
jobject thisTerrain, thisWall, thisFluid;
|
||||||
|
vec4 fluidAmountToCol;
|
||||||
|
vec4 thisTileLuminosity, thisTileOpacity, thisTileOpacity2, sunLight;
|
||||||
|
|
||||||
|
const vec4 lightMagic = _mm_set1_ps(8);
|
||||||
|
const vec4 oneVec = _mm_set1_ps(1);
|
||||||
|
const vec4 zeroVec = _mm_set1_ps(0);
|
||||||
|
const vec4 sqrt2Vec = _mm_set1_ps(1.41421356f);
|
||||||
|
|
||||||
|
int LIGHTMAP_WIDTH;
|
||||||
|
int for_x_start = 0;
|
||||||
|
int for_y_start = 0;
|
||||||
|
int for_x_end = 0;
|
||||||
|
int for_y_end = 0;
|
||||||
|
|
||||||
|
void getLightsAndShades(JNIEnv *env, jclass cls, jobject obj, int x, int y) {
|
||||||
|
// get world first
|
||||||
|
jfieldID fid = (*env)->GetFieldID(env, cls, "world", "Lnet/torvald/terrarum/gameworld/GameWorld;");
|
||||||
|
jobject worldobj = (*env)->GetObjectField(env, obj, fid);
|
||||||
|
jclass worldcls = (*env)->GetObjectClass(env, worldobj);
|
||||||
|
|
||||||
|
// get method IDs
|
||||||
|
jmethodID getTerrain_mid = (*env)->GetMethodID(env, worldcls, "getTileFromTerrain", "(II)Ljava/lang/Integer;")
|
||||||
|
jmethodID getFluid_mid = (*env)->GetMethodID(env, worldcls, "getFluid", "(II)Lnet/torvald/terrarum/gameworld/GameWorld$FluidInfo;")
|
||||||
|
jmethodID getWall_mid = (*env)->GetMethodID(env, worldcls, "getTileFromWall", "(II)Ljava/lang/Integer;")
|
||||||
|
jmethodID getSun_mid = (*env)->GetMethodID(env, worldcls, "getGlobalLight", "()Lcom/badlogic/gdx/graphics/Color;")
|
||||||
|
|
||||||
|
// populate the vars
|
||||||
|
thisTerrain = (*env)->CallObjectMethod(env, worldobj, getTerrain_mid, x, y);
|
||||||
|
thisFluid = (*env)->CallObjectMethod(env, worldobj, getFluid_mid, x, y);
|
||||||
|
thisWall = (*env)->CallObjectMethod(env, worldobj, getWall_mid, x, y);
|
||||||
|
|
||||||
|
// set sunlight
|
||||||
|
jobject sun_gdxcolor = (*env)->CallObjectMethod(env, worldobj, getSun_mid, x, y);
|
||||||
|
jclass gdxcolor_cls = (*env)->GetObjectClass(env, sun_gdxcolor);
|
||||||
|
jfieldID sunfid = (*env)->GetFieldID(env, gdxcolor_cls, "r", "F");
|
||||||
|
float sunr = GetFloatField(env, sun_gdxcolor, sunfid) * DIV_FLOAT;
|
||||||
|
sunfid = (*env)->GetFieldID(env, gdxcolor_cls, "g", "F");
|
||||||
|
float sung = GetFloatField(env, sun_gdxcolor, sunfid) * DIV_FLOAT;
|
||||||
|
sunfid = (*env)->GetFieldID(env, gdxcolor_cls, "b", "F");
|
||||||
|
float sunb = GetFloatField(env, sun_gdxcolor, sunfid) * DIV_FLOAT;
|
||||||
|
sunfid = (*env)->GetFieldID(env, gdxcolor_cls, "a", "F");
|
||||||
|
float suna = GetFloatField(env, sun_gdxcolor, sunfid) * DIV_FLOAT;
|
||||||
|
|
||||||
|
// TODO check rgba argument order (RGBA or ABGR, intel doc is confusing)
|
||||||
|
sunLight = _mm_set_ps(sunr, sung, sunb, suna);
|
||||||
|
|
||||||
|
// get fluid info
|
||||||
|
jclass fluid_cls = (*env)->GetObjectClass(env, thisFluid);
|
||||||
|
jfieldID fluidfid = (*env)->GetFieldID(env, fluid_cls, "type", "I");
|
||||||
|
jint fluidType = GetIntField(env, thisFluid, fluidfid);
|
||||||
|
fluidfid = (*env)->GetFieldID(env, fluid_cls, "amount", "F");
|
||||||
|
jfloat fluidFill = GetIntField(env, thisFluid, fluidfid);
|
||||||
|
|
||||||
|
|
||||||
|
lightLevelThis = _mm_set1_ps(0);
|
||||||
|
|
||||||
|
if (fluidType != 0) {
|
||||||
|
fluidAmountToCol = _mm_set1_ps(fluidFill);
|
||||||
|
thisTileLuminosity =
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 _mm_darken_ps(vec4 data, vec4 darken) {
|
||||||
|
// data * (1f - darken * lightMagic)
|
||||||
|
return _mm_mul_ps(
|
||||||
|
data,
|
||||||
|
_mm_sub_ps(
|
||||||
|
oneVec,
|
||||||
|
mm_mul_ps(
|
||||||
|
darken,
|
||||||
|
lightMagic
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 calculate(int x, int y) {
|
||||||
|
getLightsAndShades(x, y);
|
||||||
|
|
||||||
|
lightLevelThis = _mm_max_ps(lightLevelThis, _mm_darken_ps(getLightInternal(x - 1, y - 1), thisTileOpacity2));
|
||||||
|
lightLevelThis = _mm_max_ps(lightLevelThis, _mm_darken_ps(getLightInternal(x + 1, y - 1), thisTileOpacity2));
|
||||||
|
lightLevelThis = _mm_max_ps(lightLevelThis, _mm_darken_ps(getLightInternal(x - 1, y + 1), thisTileOpacity2));
|
||||||
|
lightLevelThis = _mm_max_ps(lightLevelThis, _mm_darken_ps(getLightInternal(x + 1, y + 1), thisTileOpacity2));
|
||||||
|
|
||||||
|
lightLevelThis = _mm_max_ps(lightLevelThis, _mm_darken_ps(getLightInternal(x , y - 1), thisTileOpacity));
|
||||||
|
lightLevelThis = _mm_max_ps(lightLevelThis, _mm_darken_ps(getLightInternal(x , y + 1), thisTileOpacity));
|
||||||
|
lightLevelThis = _mm_max_ps(lightLevelThis, _mm_darken_ps(getLightInternal(x - 1, y ), thisTileOpacity));
|
||||||
|
lightLevelThis = _mm_max_ps(lightLevelThis, _mm_darken_ps(getLightInternal(x + 1, y ), thisTileOpacity));
|
||||||
|
|
||||||
|
return lightLevelThis
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLightOf(JNIEnv *env, jclass cls, int x, int y, vec4 colour) {
|
||||||
|
if (y - for_y_start + overscan_open >= 0 &&
|
||||||
|
y - for_y_start + overscan_open < LIGHTMAP_HEIGHT &&
|
||||||
|
|
||||||
|
x - for_x_start + overscan_open >= 0 &&
|
||||||
|
x - for_x_start + overscan_open < LIGHTMAP_WIDTH) {
|
||||||
|
|
||||||
|
int ypos = y - for_y_start + overscan_open
|
||||||
|
int xpos = x - for_x_start + overscan_open
|
||||||
|
|
||||||
|
// unpack our vector
|
||||||
|
float[4] vector;
|
||||||
|
_mm_store_ps(vector *, colour);
|
||||||
|
|
||||||
|
// get the array
|
||||||
|
jfieldID fid = (*env)->GetFieldID(env, cls, "lightmap", "F");
|
||||||
|
jfloat * list = (*env)->GetFloatArrayElements(env, jfloatarray, null);
|
||||||
|
|
||||||
|
// set r/g/b/a values
|
||||||
|
list[4 * (ypos * LIGHTMAP_WIDTH + xpos)] = vector[0];
|
||||||
|
list[4 * (ypos * LIGHTMAP_WIDTH + xpos) + 1] = vector[1];
|
||||||
|
list[4 * (ypos * LIGHTMAP_WIDTH + xpos) + 2] = vector[2];
|
||||||
|
list[4 * (ypos * LIGHTMAP_WIDTH + xpos) + 3] = vector[3];
|
||||||
|
|
||||||
|
//list[4 * ypos * LIGHTMAP_WIDTH + xpos] = colour
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 getLightInternal(JNIEnv *env, jobject obj, int x, int y) {
|
||||||
|
if (y - for_y_start + overscan_open >= 0 &&
|
||||||
|
y - for_y_start + overscan_open < LIGHTMAP_HEIGHT &&
|
||||||
|
|
||||||
|
x - for_x_start + overscan_open >= 0 &&
|
||||||
|
x - for_x_start + overscan_open < LIGHTMAP_WIDTH) {
|
||||||
|
|
||||||
|
int ypos = y - for_y_start + overscan_open
|
||||||
|
int xpos = x - for_x_start + overscan_open
|
||||||
|
|
||||||
|
// get the array
|
||||||
|
jclass cls = (*env)->GetObjectClass(env, obj);
|
||||||
|
jfieldID fid = (*env)->GetFieldID(env, cls, "lightmap", "F");
|
||||||
|
jfloat * list = (*env)->GetFloatArrayElements(env, jfloatarray, null);
|
||||||
|
|
||||||
|
float r = list[4 * (ypos * LIGHTMAP_WIDTH + xpos)];
|
||||||
|
float g = list[4 * (ypos * LIGHTMAP_WIDTH + xpos) + 1];
|
||||||
|
float b = list[4 * (ypos * LIGHTMAP_WIDTH + xpos) + 2];
|
||||||
|
float a = list[4 * (ypos * LIGHTMAP_WIDTH + xpos) + 3];
|
||||||
|
|
||||||
|
// TODO check rgba argument order (RGBA or ABGR, intel doc is confusing)
|
||||||
|
return _mm_set_ps(r, g, b, a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_net_torvald_terrarum_worlddrawer_LightmapRenderer_fireRecalculateEventJNI(JNIEnv *env, jobject object) {
|
||||||
|
// update (read) variables
|
||||||
|
jclass cls = (*env)->GetObjectClass(env, object);
|
||||||
|
jfieldID fid = (*env)->GetFieldID(env, cls, "LIGHTMAP_WIDTH", "I");
|
||||||
|
LIGHTMAP_WIDTH = (*env)->GetIntField(env, obj, fid);
|
||||||
|
fid = (*env)->GetFieldID(env, cls, "for_x_start", "I");
|
||||||
|
for_x_start = (*env)->GetIntField(env, obj, fid);
|
||||||
|
fid = (*env)->GetFieldID(env, cls, "for_y_start", "I");
|
||||||
|
for_y_start = (*env)->GetIntField(env, obj, fid);
|
||||||
|
fid = (*env)->GetFieldID(env, cls, "for_x_end", "I");
|
||||||
|
for_x_end = (*env)->GetIntField(env, obj, fid);
|
||||||
|
fid = (*env)->GetFieldID(env, cls, "for_y_end", "I");
|
||||||
|
for_y_end = (*env)->GetIntField(env, obj, fid);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updating order:
|
||||||
|
* ,--------. ,--+-----. ,-----+--. ,--------. -
|
||||||
|
* |↘ | | | 3| |3 | | | ↙| ↕︎ overscan_open / overscan_opaque
|
||||||
|
* | ,-----+ | | 2 | | 2 | | +-----. | - depending on the noop_mask
|
||||||
|
* | |1 | | |1 | | 1| | | 1| |
|
||||||
|
* | | 2 | | `-----+ +-----' | | 2 | |
|
||||||
|
* | | 3| |↗ | | ↖| |3 | |
|
||||||
|
* `--+-----' `--------' `--------' `-----+--'
|
||||||
|
* round: 1 2 3 4
|
||||||
|
* Run in this order: 0-2-3-4-1
|
||||||
|
* zero means we wipe out lightmap.
|
||||||
|
* But why start from 2? No special reason.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
@@ -12,14 +12,12 @@ import net.torvald.terrarum.blockproperties.Block
|
|||||||
import net.torvald.terrarum.blockproperties.BlockCodex
|
import net.torvald.terrarum.blockproperties.BlockCodex
|
||||||
import net.torvald.terrarum.blockproperties.Fluid
|
import net.torvald.terrarum.blockproperties.Fluid
|
||||||
import net.torvald.terrarum.concurrent.ParallelUtils.sliceEvenly
|
import net.torvald.terrarum.concurrent.ParallelUtils.sliceEvenly
|
||||||
import net.torvald.terrarum.concurrent.ThreadParallel
|
|
||||||
import net.torvald.terrarum.gameactors.ActorWBMovable
|
import net.torvald.terrarum.gameactors.ActorWBMovable
|
||||||
import net.torvald.terrarum.gameactors.Luminous
|
import net.torvald.terrarum.gameactors.Luminous
|
||||||
import net.torvald.terrarum.gameworld.BlockAddress
|
import net.torvald.terrarum.gameworld.BlockAddress
|
||||||
import net.torvald.terrarum.gameworld.GameWorld
|
import net.torvald.terrarum.gameworld.GameWorld
|
||||||
import net.torvald.terrarum.modulebasegame.IngameRenderer
|
import net.torvald.terrarum.modulebasegame.IngameRenderer
|
||||||
import net.torvald.terrarum.realestate.LandUtil
|
import net.torvald.terrarum.realestate.LandUtil
|
||||||
import java.util.concurrent.atomic.AtomicReferenceArray
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sub-portion of IngameRenderer. You are not supposed to directly deal with this.
|
* Sub-portion of IngameRenderer. You are not supposed to directly deal with this.
|
||||||
@@ -53,7 +51,7 @@ object LightmapRenderer {
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
for (i in 0 until lightmap.size) {
|
for (i in 0 until lightmap.size) {
|
||||||
lightmap[i] = colourNull
|
lightmap[i] = 0f
|
||||||
}
|
}
|
||||||
|
|
||||||
makeUpdateTaskList()
|
makeUpdateTaskList()
|
||||||
@@ -84,7 +82,7 @@ object LightmapRenderer {
|
|||||||
*/
|
*/
|
||||||
// it utilises alpha channel to determine brightness of "glow" sprites (so that alpha channel works like UV light)
|
// it utilises alpha channel to determine brightness of "glow" sprites (so that alpha channel works like UV light)
|
||||||
//private val lightmap: Array<Array<Color>> = Array(LIGHTMAP_HEIGHT) { Array(LIGHTMAP_WIDTH, { Color(0f,0f,0f,0f) }) } // Can't use framebuffer/pixmap -- this is a fvec4 array, whereas they are ivec4.
|
//private val lightmap: Array<Array<Color>> = Array(LIGHTMAP_HEIGHT) { Array(LIGHTMAP_WIDTH, { Color(0f,0f,0f,0f) }) } // Can't use framebuffer/pixmap -- this is a fvec4 array, whereas they are ivec4.
|
||||||
private val lightmap: Array<Color> = Array(LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT) { Color(0f,0f,0f,0f) } // Can't use framebuffer/pixmap -- this is a fvec4 array, whereas they are ivec4.
|
private val lightmap: Array<Float> = Array(LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT * 4) { 0f } // Can't use framebuffer/pixmap -- this is a fvec4 array, whereas they are ivec4.
|
||||||
private val lanternMap = HashMap<BlockAddress, Color>((Terrarum.ingame?.ACTORCONTAINER_INITIAL_SIZE ?: 2) * 4)
|
private val lanternMap = HashMap<BlockAddress, Color>((Terrarum.ingame?.ACTORCONTAINER_INITIAL_SIZE ?: 2) * 4)
|
||||||
|
|
||||||
private lateinit var texturedLightMap: FrameBuffer
|
private lateinit var texturedLightMap: FrameBuffer
|
||||||
@@ -202,7 +200,12 @@ object LightmapRenderer {
|
|||||||
val xpos = x - for_x_start + overscan_open
|
val xpos = x - for_x_start + overscan_open
|
||||||
|
|
||||||
//return lightmap[ypos][xpos]
|
//return lightmap[ypos][xpos]
|
||||||
return lightmap[ypos * LIGHTMAP_WIDTH + xpos]
|
return Color(
|
||||||
|
lightmap[4 * (ypos * LIGHTMAP_WIDTH + xpos)],
|
||||||
|
lightmap[4 * (ypos * LIGHTMAP_WIDTH + xpos) + 1],
|
||||||
|
lightmap[4 * (ypos * LIGHTMAP_WIDTH + xpos) + 2],
|
||||||
|
lightmap[4 * (ypos * LIGHTMAP_WIDTH + xpos) + 3]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
@@ -221,7 +224,7 @@ object LightmapRenderer {
|
|||||||
* @param colour Color to write
|
* @param colour Color to write
|
||||||
* @param applyFun A function ```foo(old_colour, given_colour)```
|
* @param applyFun A function ```foo(old_colour, given_colour)```
|
||||||
*/
|
*/
|
||||||
private fun setLightOf(list: Array<Color>, x: Int, y: Int, colour: Color, applyFun: (Color, Color) -> Color = { _, c -> c }) {
|
private fun setLightOf(list: Array<Float>, x: Int, y: Int, colour: Color) {
|
||||||
if (y - for_y_start + overscan_open in 0 until LIGHTMAP_HEIGHT &&
|
if (y - for_y_start + overscan_open in 0 until LIGHTMAP_HEIGHT &&
|
||||||
x - for_x_start + overscan_open in 0 until LIGHTMAP_WIDTH) {
|
x - for_x_start + overscan_open in 0 until LIGHTMAP_WIDTH) {
|
||||||
|
|
||||||
@@ -229,7 +232,12 @@ object LightmapRenderer {
|
|||||||
val xpos = x - for_x_start + overscan_open
|
val xpos = x - for_x_start + overscan_open
|
||||||
|
|
||||||
//lightmap[ypos][xpos] = applyFun.invoke(list[ypos][xpos], colour)
|
//lightmap[ypos][xpos] = applyFun.invoke(list[ypos][xpos], colour)
|
||||||
list[ypos * LIGHTMAP_WIDTH + xpos] = applyFun.invoke(list[ypos * LIGHTMAP_WIDTH + xpos], colour)
|
//list[ypos * LIGHTMAP_WIDTH + xpos] = applyFun.invoke(list[ypos * LIGHTMAP_WIDTH + xpos], colour)
|
||||||
|
|
||||||
|
list[4 * (ypos * LIGHTMAP_WIDTH + xpos)] = colour.r
|
||||||
|
list[4 * (ypos * LIGHTMAP_WIDTH + xpos) + 1] = colour.g
|
||||||
|
list[4 * (ypos * LIGHTMAP_WIDTH + xpos) + 2] = colour.b
|
||||||
|
list[4 * (ypos * LIGHTMAP_WIDTH + xpos) + 3] = colour.a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,189 +270,98 @@ object LightmapRenderer {
|
|||||||
buildLanternmap()
|
buildLanternmap()
|
||||||
} // usually takes 3000 ns
|
} // usually takes 3000 ns
|
||||||
|
|
||||||
if (!SHADER_LIGHTING) {
|
|
||||||
/**
|
|
||||||
* Updating order:
|
|
||||||
* ,--------. ,--+-----. ,-----+--. ,--------. -
|
|
||||||
* |↘ | | | 3| |3 | | | ↙| ↕︎ overscan_open / overscan_opaque
|
|
||||||
* | ,-----+ | | 2 | | 2 | | +-----. | - depending on the noop_mask
|
|
||||||
* | |1 | | |1 | | 1| | | 1| |
|
|
||||||
* | | 2 | | `-----+ +-----' | | 2 | |
|
|
||||||
* | | 3| |↗ | | ↖| |3 | |
|
|
||||||
* `--+-----' `--------' `--------' `-----+--'
|
|
||||||
* round: 1 2 3 4
|
|
||||||
* for all lightmap[y][x], run in this order: 2-3-4-1-2
|
|
||||||
* If you run only 4 sets, orthogonal/diagonal artefacts are bound to occur,
|
|
||||||
* it seems 5-pass is mandatory
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// wipe out lightmap
|
fireRecalculateEventJava()
|
||||||
AppLoader.measureDebugTime("Renderer.Light0") {
|
|
||||||
//for (ky in 0 until lightmap.size) for (kx in 0 until lightmap[0].size) lightmap[ky][kx] = colourNull
|
|
||||||
for (k in 0 until lightmap.size) lightmap[k] = colourNull
|
|
||||||
// when disabled, light will "decay out" instead of "instantly out", which can have a cool effect
|
|
||||||
// but the performance boost is measly 0.1 ms on 6700K
|
|
||||||
}
|
|
||||||
// O((5*9)n) == O(n) where n is a size of the map.
|
|
||||||
// Because of inevitable overlaps on the area, it only works with MAX blend
|
|
||||||
|
|
||||||
|
|
||||||
// each usually takes 8 000 000..12 000 000 miliseconds total when not threaded
|
|
||||||
|
|
||||||
if (!AppLoader.getConfigBoolean("multithreadedlight")) {
|
|
||||||
//val workMap = Array(lightmap.size) { colourNull }
|
|
||||||
|
|
||||||
// The skipping is dependent on how you get ambient light,
|
|
||||||
// in this case we have 'spillage' due to the fact calculate() samples 3x3 area.
|
|
||||||
|
|
||||||
// FIXME theoretically skipping shouldn't work (light can be anywhere on the screen, not just centre
|
|
||||||
// but how does it actually work ?!?!?!!?!?!?!?
|
|
||||||
// because things are filled in subsequent frames ?
|
|
||||||
// because of not wiping out prev map ! (if pass=1 also calculates ambience, was disabled to not have to wipe out)
|
|
||||||
|
|
||||||
// Round 2
|
|
||||||
AppLoader.measureDebugTime("Renderer.Light1") {
|
|
||||||
for (y in for_y_end + overscan_open downTo for_y_start) {
|
|
||||||
for (x in for_x_start - overscan_open..for_x_end) {
|
|
||||||
setLightOf(lightmap, x, y, calculate(x, y))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Round 3
|
|
||||||
AppLoader.measureDebugTime("Renderer.Light2") {
|
|
||||||
for (y in for_y_end + overscan_open downTo for_y_start) {
|
|
||||||
for (x in for_x_end + overscan_open downTo for_x_start) {
|
|
||||||
setLightOf(lightmap, x, y, calculate(x, y))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Round 4
|
|
||||||
AppLoader.measureDebugTime("Renderer.Light3") {
|
|
||||||
for (y in for_y_start - overscan_open..for_y_end) {
|
|
||||||
for (x in for_x_end + overscan_open downTo for_x_start) {
|
|
||||||
setLightOf(lightmap, x, y, calculate(x, y))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Round 1
|
|
||||||
AppLoader.measureDebugTime("Renderer.Light4") {
|
|
||||||
for (y in for_y_start - overscan_open..for_y_end) {
|
|
||||||
for (x in for_x_start - overscan_open..for_x_end) {
|
|
||||||
setLightOf(lightmap, x, y, calculate(x, y))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AppLoader.addDebugTime("Renderer.LightTotal",
|
|
||||||
"Renderer.Light1",
|
|
||||||
"Renderer.Light2",
|
|
||||||
"Renderer.Light3",
|
|
||||||
"Renderer.Light4",
|
|
||||||
"Renderer.Light0"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else if (world.worldIndex != -1) { // to avoid updating on the null world
|
|
||||||
val buf = AtomicReferenceArray<Color>(lightmap.size)
|
|
||||||
|
|
||||||
AppLoader.measureDebugTime("Renderer.LightPrlPre") {
|
|
||||||
// update the content of buf using maxBlend -- it's not meant for overwrite
|
|
||||||
|
|
||||||
updateMessages.forEachIndexed { index, msg ->
|
|
||||||
ThreadParallel.map(index, "Light") {
|
|
||||||
// for the message slices...
|
|
||||||
msg.forEach { m ->
|
|
||||||
// update the content of buf using maxBlend -- it's not meant for overwrite
|
|
||||||
buf.getAndUpdate(m.y * LIGHTMAP_WIDTH + m.x) { oldCol ->
|
|
||||||
val ux = m.x + for_x_start - overscan_open
|
|
||||||
val uy = m.y + for_y_start - overscan_open
|
|
||||||
|
|
||||||
(oldCol ?: colourNull) maxBlend calculate(ux, uy)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AppLoader.measureDebugTime("Renderer.LightPrlRun") {
|
|
||||||
ThreadParallel.startAllWaitForDie()
|
|
||||||
}
|
|
||||||
|
|
||||||
AppLoader.measureDebugTime("Renderer.LightPrlPost") {
|
|
||||||
// copy to lightmap
|
|
||||||
for (k in 0 until lightmap.size) {
|
|
||||||
lightmap[k] = buf.getPlain(k) ?: colourNull
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AppLoader.addDebugTime("Renderer.LightTotal",
|
|
||||||
"Renderer.LightPrlPre",
|
|
||||||
"Renderer.LightPrlRun",
|
|
||||||
"Renderer.LightPrlPost"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
AppLoader.measureDebugTime("Renderer.LightGPU") {
|
|
||||||
|
|
||||||
// prepare necessary textures (lightmap, shademap) for the input.
|
|
||||||
for (ty in 0 until LIGHTMAP_HEIGHT) {
|
|
||||||
for (tx in 0 until LIGHTMAP_WIDTH) {
|
|
||||||
val wx = tx + for_x_start - overscan_open
|
|
||||||
val wy = ty + for_y_start - overscan_open
|
|
||||||
|
|
||||||
// Several variables will be altered by this. See its documentation.
|
|
||||||
getLightsAndShades(wx, wy)
|
|
||||||
|
|
||||||
texturedLightSourcePixmap.drawPixel(tx, ty, lightLevelThis.toRGBA())
|
|
||||||
texturedShadeSourcePixmap.drawPixel(tx, ty, thisTileOpacity.toRGBA())
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*if (wy in for_y_start..for_y_end && wx in for_x_start..for_x_end) {
|
|
||||||
texturedLightSourcePixmap.drawPixel(tx, ty, 0x00FFFFFF)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
texturedLightSourcePixmap.drawPixel(tx, ty, 0xFF000000.toInt())
|
|
||||||
}*/
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
texturedLightSources.dispose()
|
|
||||||
texturedLightSources = Texture(texturedLightSourcePixmap)
|
|
||||||
|
|
||||||
texturedShadeSources.dispose()
|
|
||||||
texturedShadeSources = Texture(texturedShadeSourcePixmap)
|
|
||||||
|
|
||||||
|
|
||||||
texturedLightMap.inAction(texturedLightCamera, null) {
|
|
||||||
gdxClearAndSetBlend(0f,0f,0f,0f)
|
|
||||||
Gdx.gl.glDisable(GL20.GL_BLEND)
|
|
||||||
|
|
||||||
texturedShadeSources.bind(SHADEMAP_UNIT)
|
|
||||||
texturedLightSources.bind(LIGHTMAP_UNIT)
|
|
||||||
|
|
||||||
lightCalcShader.begin()
|
|
||||||
// it seems that every time shader ends, uniforms reset themselves.
|
|
||||||
lightCalcShader.setUniformMatrix("u_projTrans", texturedLightCamera.combined)
|
|
||||||
lightCalcShader.setUniformf("outSize", LIGHTMAP_WIDTH.toFloat(), LIGHTMAP_HEIGHT.toFloat())
|
|
||||||
lightCalcShader.setUniformi("shades", SHADEMAP_UNIT)
|
|
||||||
lightCalcShader.setUniformi("lights", LIGHTMAP_UNIT)
|
|
||||||
texturedLightQuad.render(lightCalcShader, GL20.GL_TRIANGLES)
|
|
||||||
lightCalcShader.end()
|
|
||||||
}
|
|
||||||
|
|
||||||
Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0) // so that batch that comes next will bind any tex to it
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun fireRecalculateEventJava() {
|
||||||
|
/**
|
||||||
|
* Updating order:
|
||||||
|
* ,--------. ,--+-----. ,-----+--. ,--------. -
|
||||||
|
* |↘ | | | 3| |3 | | | ↙| ↕︎ overscan_open / overscan_opaque
|
||||||
|
* | ,-----+ | | 2 | | 2 | | +-----. | - depending on the noop_mask
|
||||||
|
* | |1 | | |1 | | 1| | | 1| |
|
||||||
|
* | | 2 | | `-----+ +-----' | | 2 | |
|
||||||
|
* | | 3| |↗ | | ↖| |3 | |
|
||||||
|
* `--+-----' `--------' `--------' `-----+--'
|
||||||
|
* round: 1 2 3 4
|
||||||
|
* Run in this order: 0-2-3-4-1
|
||||||
|
* zero means we wipe out lightmap.
|
||||||
|
* But why start from 2? No special reason.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// wipe out lightmap
|
||||||
|
AppLoader.measureDebugTime("Renderer.Light0") {
|
||||||
|
//for (ky in 0 until lightmap.size) for (kx in 0 until lightmap[0].size) lightmap[ky][kx] = colourNull
|
||||||
|
for (k in 0 until lightmap.size) lightmap[k] = 0f
|
||||||
|
// when disabled, light will "decay out" instead of "instantly out", which can have a cool effect
|
||||||
|
// but the performance boost is measly 0.1 ms on 6700K
|
||||||
|
}
|
||||||
|
// O((5*9)n) == O(n) where n is a size of the map.
|
||||||
|
// Because of inevitable overlaps on the area, it only works with MAX blend
|
||||||
|
|
||||||
|
|
||||||
|
// each usually takes 8 000 000..12 000 000 miliseconds total when not threaded
|
||||||
|
|
||||||
|
//val workMap = Array(lightmap.size) { colourNull }
|
||||||
|
|
||||||
|
// The skipping is dependent on how you get ambient light,
|
||||||
|
// in this case we have 'spillage' due to the fact calculate() samples 3x3 area.
|
||||||
|
|
||||||
|
// FIXME theoretically skipping shouldn't work (light can be anywhere on the screen, not just centre
|
||||||
|
// but how does it actually work ?!?!?!!?!?!?!?
|
||||||
|
// because things are filled in subsequent frames ?
|
||||||
|
// because of not wiping out prev map ! (if pass=1 also calculates ambience, was disabled to not have to wipe out)
|
||||||
|
|
||||||
|
// Round 2
|
||||||
|
AppLoader.measureDebugTime("Renderer.Light1") {
|
||||||
|
for (y in for_y_end + overscan_open downTo for_y_start) {
|
||||||
|
for (x in for_x_start - overscan_open..for_x_end) {
|
||||||
|
setLightOf(lightmap, x, y, calculate(x, y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Round 3
|
||||||
|
AppLoader.measureDebugTime("Renderer.Light2") {
|
||||||
|
for (y in for_y_end + overscan_open downTo for_y_start) {
|
||||||
|
for (x in for_x_end + overscan_open downTo for_x_start) {
|
||||||
|
setLightOf(lightmap, x, y, calculate(x, y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Round 4
|
||||||
|
AppLoader.measureDebugTime("Renderer.Light3") {
|
||||||
|
for (y in for_y_start - overscan_open..for_y_end) {
|
||||||
|
for (x in for_x_end + overscan_open downTo for_x_start) {
|
||||||
|
setLightOf(lightmap, x, y, calculate(x, y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Round 1
|
||||||
|
AppLoader.measureDebugTime("Renderer.Light4") {
|
||||||
|
for (y in for_y_start - overscan_open..for_y_end) {
|
||||||
|
for (x in for_x_start - overscan_open..for_x_end) {
|
||||||
|
setLightOf(lightmap, x, y, calculate(x, y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AppLoader.addDebugTime("Renderer.LightTotal",
|
||||||
|
"Renderer.Light1",
|
||||||
|
"Renderer.Light2",
|
||||||
|
"Renderer.Light3",
|
||||||
|
"Renderer.Light4",
|
||||||
|
"Renderer.Light0"
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private external fun fireRecalculateEventJNI()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -660,47 +577,41 @@ object LightmapRenderer {
|
|||||||
val this_y_end = for_y_end// + overscan_open
|
val this_y_end = for_y_end// + overscan_open
|
||||||
|
|
||||||
// wipe out beforehand. You DO need this
|
// wipe out beforehand. You DO need this
|
||||||
if (!SHADER_LIGHTING) {
|
lightBuffer.blending = Pixmap.Blending.None // gonna overwrite (remove this line causes the world to go bit darker)
|
||||||
lightBuffer.blending = Pixmap.Blending.None // gonna overwrite (remove this line causes the world to go bit darker)
|
lightBuffer.setColor(colourNull)
|
||||||
lightBuffer.setColor(colourNull)
|
lightBuffer.fill()
|
||||||
lightBuffer.fill()
|
|
||||||
|
|
||||||
|
|
||||||
// write to colour buffer
|
// write to colour buffer
|
||||||
for (y in this_y_start..this_y_end) {
|
for (y in this_y_start..this_y_end) {
|
||||||
//println("y: $y, this_y_start: $this_y_start")
|
//println("y: $y, this_y_start: $this_y_start")
|
||||||
if (y == this_y_start && this_y_start == 0) {
|
if (y == this_y_start && this_y_start == 0) {
|
||||||
//throw Error("Fuck hits again...")
|
//throw Error("Fuck hits again...")
|
||||||
}
|
|
||||||
|
|
||||||
for (x in this_x_start..this_x_end) {
|
|
||||||
|
|
||||||
val color = (getLightForOpaque(x, y) ?: Color(0f, 0f, 0f, 0f)).normaliseToHDR()
|
|
||||||
|
|
||||||
lightBuffer.setColor(color)
|
|
||||||
|
|
||||||
//lightBuffer.drawPixel(x - this_x_start, y - this_y_start)
|
|
||||||
|
|
||||||
lightBuffer.drawPixel(x - this_x_start, lightBuffer.height - 1 - y + this_y_start) // flip Y
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (x in this_x_start..this_x_end) {
|
||||||
|
|
||||||
// draw to the batch
|
val color = (getLightForOpaque(x, y) ?: Color(0f, 0f, 0f, 0f)).normaliseToHDR()
|
||||||
_lightBufferAsTex.dispose()
|
|
||||||
_lightBufferAsTex = Texture(lightBuffer)
|
|
||||||
_lightBufferAsTex.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest)
|
|
||||||
|
|
||||||
|
lightBuffer.setColor(color)
|
||||||
|
|
||||||
Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0) // so that batch that comes next will bind any tex to it
|
//lightBuffer.drawPixel(x - this_x_start, y - this_y_start)
|
||||||
// we might not need shader here...
|
|
||||||
//batch.draw(lightBufferAsTex, 0f, 0f, lightBufferAsTex.width.toFloat(), lightBufferAsTex.height.toFloat())
|
lightBuffer.drawPixel(x - this_x_start, lightBuffer.height - 1 - y + this_y_start) // flip Y
|
||||||
batch.draw(_lightBufferAsTex, 0f, 0f, _lightBufferAsTex.width * DRAW_TILE_SIZE, _lightBufferAsTex.height * DRAW_TILE_SIZE)
|
}
|
||||||
}
|
|
||||||
else {
|
|
||||||
Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0) // so that batch that comes next will bind any tex to it
|
|
||||||
batch.draw(texturedLightMap.colorBufferTexture, -overscan_open * DRAW_TILE_SIZE, -overscan_open * DRAW_TILE_SIZE, texturedLightMap.width * DRAW_TILE_SIZE, texturedLightMap.height * DRAW_TILE_SIZE)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// draw to the batch
|
||||||
|
_lightBufferAsTex.dispose()
|
||||||
|
_lightBufferAsTex = Texture(lightBuffer)
|
||||||
|
_lightBufferAsTex.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest)
|
||||||
|
|
||||||
|
|
||||||
|
Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0) // so that batch that comes next will bind any tex to it
|
||||||
|
// we might not need shader here...
|
||||||
|
//batch.draw(lightBufferAsTex, 0f, 0f, lightBufferAsTex.width.toFloat(), lightBufferAsTex.height.toFloat())
|
||||||
|
batch.draw(_lightBufferAsTex, 0f, 0f, _lightBufferAsTex.width * DRAW_TILE_SIZE, _lightBufferAsTex.height * DRAW_TILE_SIZE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1014,10 +925,12 @@ object LightmapRenderer {
|
|||||||
for (x in overscan_open..render_width + overscan_open + 1) {
|
for (x in overscan_open..render_width + overscan_open + 1) {
|
||||||
try {
|
try {
|
||||||
//val colour = lightmap[y][x]
|
//val colour = lightmap[y][x]
|
||||||
val colour = lightmap[y * LIGHTMAP_WIDTH + x]
|
val colourR = lightmap[4 * (y * LIGHTMAP_WIDTH + x)]
|
||||||
reds[minOf(CHANNEL_MAX, colour.r.times(MUL).floorInt())] += 1
|
val colourG = lightmap[4 * (y * LIGHTMAP_WIDTH + x) + 1]
|
||||||
greens[minOf(CHANNEL_MAX, colour.g.times(MUL).floorInt())] += 1
|
val colourB = lightmap[4 * (y * LIGHTMAP_WIDTH + x) + 2]
|
||||||
blues[minOf(CHANNEL_MAX, colour.b.times(MUL).floorInt())] += 1
|
reds[minOf(CHANNEL_MAX, colourR.times(MUL).floorInt())] += 1
|
||||||
|
greens[minOf(CHANNEL_MAX, colourG.times(MUL).floorInt())] += 1
|
||||||
|
blues[minOf(CHANNEL_MAX, colourB.times(MUL).floorInt())] += 1
|
||||||
}
|
}
|
||||||
catch (e: ArrayIndexOutOfBoundsException) { }
|
catch (e: ArrayIndexOutOfBoundsException) { }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||||
|
#include <jni.h>
|
||||||
|
/* Header for class net_torvald_terrarum_worlddrawer_LightmapRenderer */
|
||||||
|
|
||||||
|
#ifndef _Included_net_torvald_terrarum_worlddrawer_LightmapRenderer
|
||||||
|
#define _Included_net_torvald_terrarum_worlddrawer_LightmapRenderer
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
#undef net_torvald_terrarum_worlddrawer_LightmapRenderer_overscan_open
|
||||||
|
#define net_torvald_terrarum_worlddrawer_LightmapRenderer_overscan_open 32L
|
||||||
|
#undef net_torvald_terrarum_worlddrawer_LightmapRenderer_overscan_opaque
|
||||||
|
#define net_torvald_terrarum_worlddrawer_LightmapRenderer_overscan_opaque 8L
|
||||||
|
#undef net_torvald_terrarum_worlddrawer_LightmapRenderer_LIGHTMAP_UNIT
|
||||||
|
#define net_torvald_terrarum_worlddrawer_LightmapRenderer_LIGHTMAP_UNIT 0L
|
||||||
|
#undef net_torvald_terrarum_worlddrawer_LightmapRenderer_SHADEMAP_UNIT
|
||||||
|
#define net_torvald_terrarum_worlddrawer_LightmapRenderer_SHADEMAP_UNIT 1L
|
||||||
|
#undef net_torvald_terrarum_worlddrawer_LightmapRenderer_AIR
|
||||||
|
#define net_torvald_terrarum_worlddrawer_LightmapRenderer_AIR 0L
|
||||||
|
#undef net_torvald_terrarum_worlddrawer_LightmapRenderer_TILE_SIZE
|
||||||
|
#define net_torvald_terrarum_worlddrawer_LightmapRenderer_TILE_SIZE 16L
|
||||||
|
#undef net_torvald_terrarum_worlddrawer_LightmapRenderer_DRAW_TILE_SIZE
|
||||||
|
#define net_torvald_terrarum_worlddrawer_LightmapRenderer_DRAW_TILE_SIZE 4.0f
|
||||||
|
#undef net_torvald_terrarum_worlddrawer_LightmapRenderer_MUL
|
||||||
|
#define net_torvald_terrarum_worlddrawer_LightmapRenderer_MUL 1024L
|
||||||
|
#undef net_torvald_terrarum_worlddrawer_LightmapRenderer_CHANNEL_MAX_DECIMAL
|
||||||
|
#define net_torvald_terrarum_worlddrawer_LightmapRenderer_CHANNEL_MAX_DECIMAL 1.0f
|
||||||
|
#undef net_torvald_terrarum_worlddrawer_LightmapRenderer_MUL_2
|
||||||
|
#define net_torvald_terrarum_worlddrawer_LightmapRenderer_MUL_2 1048576L
|
||||||
|
#undef net_torvald_terrarum_worlddrawer_LightmapRenderer_CHANNEL_MAX
|
||||||
|
#define net_torvald_terrarum_worlddrawer_LightmapRenderer_CHANNEL_MAX 1023L
|
||||||
|
#undef net_torvald_terrarum_worlddrawer_LightmapRenderer_CHANNEL_MAX_FLOAT
|
||||||
|
#define net_torvald_terrarum_worlddrawer_LightmapRenderer_CHANNEL_MAX_FLOAT 1023.0f
|
||||||
|
#undef net_torvald_terrarum_worlddrawer_LightmapRenderer_COLOUR_RANGE_SIZE
|
||||||
|
#define net_torvald_terrarum_worlddrawer_LightmapRenderer_COLOUR_RANGE_SIZE 1073741824L
|
||||||
|
#undef net_torvald_terrarum_worlddrawer_LightmapRenderer_MUL_FLOAT
|
||||||
|
#define net_torvald_terrarum_worlddrawer_LightmapRenderer_MUL_FLOAT 4.0f
|
||||||
|
#undef net_torvald_terrarum_worlddrawer_LightmapRenderer_DIV_FLOAT
|
||||||
|
#define net_torvald_terrarum_worlddrawer_LightmapRenderer_DIV_FLOAT 0.25f
|
||||||
|
#undef net_torvald_terrarum_worlddrawer_LightmapRenderer_DRAW_FOR_RGB
|
||||||
|
#define net_torvald_terrarum_worlddrawer_LightmapRenderer_DRAW_FOR_RGB 65520L
|
||||||
|
#undef net_torvald_terrarum_worlddrawer_LightmapRenderer_DRAW_FOR_ALPHA
|
||||||
|
#define net_torvald_terrarum_worlddrawer_LightmapRenderer_DRAW_FOR_ALPHA 15L
|
||||||
|
#undef net_torvald_terrarum_worlddrawer_LightmapRenderer_epsilon
|
||||||
|
#define net_torvald_terrarum_worlddrawer_LightmapRenderer_epsilon 9.765625E-4f
|
||||||
|
#undef net_torvald_terrarum_worlddrawer_LightmapRenderer_lightScalingMagic
|
||||||
|
#define net_torvald_terrarum_worlddrawer_LightmapRenderer_lightScalingMagic 8.0f
|
||||||
|
/*
|
||||||
|
* Class: net_torvald_terrarum_worlddrawer_LightmapRenderer
|
||||||
|
* Method: fireRecalculateEventJNI
|
||||||
|
* Signature: ()V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_net_torvald_terrarum_worlddrawer_LightmapRenderer_fireRecalculateEventJNI
|
||||||
|
(JNIEnv *, jobject);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user