Add Skybox plugin
This commit is contained in:
@@ -849,6 +849,8 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
|
||||
lastAntiAliasingMode = antiAliasingMode;
|
||||
|
||||
// Clear scene
|
||||
int sky = client.getSkyboxColor();
|
||||
gl.glClearColor((sky >> 16 & 0xFF) / 255f, (sky >> 8 & 0xFF) / 255f, (sky & 0xFF) / 255f, 1f);
|
||||
gl.glClear(gl.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
// Upload buffers
|
||||
|
||||
@@ -0,0 +1,510 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Abex
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.plugins.skybox;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.util.Arrays;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import net.runelite.api.Client;
|
||||
|
||||
class Skybox
|
||||
{
|
||||
@FunctionalInterface
|
||||
public interface ChunkMapper
|
||||
{
|
||||
/**
|
||||
* Gets the instance template chunk data for the specified point
|
||||
*
|
||||
* @see Client#getInstanceTemplateChunks
|
||||
*/
|
||||
int getTemplateChunk(int cx, int cy, int plane);
|
||||
}
|
||||
|
||||
private static final double SQRT2 = Math.sqrt(2);
|
||||
|
||||
// How many stddev per direction we need to stay visibly continuous
|
||||
// 511/512 accuracy
|
||||
private static final double BLEND_DISTRIBUTION = 3.075;
|
||||
|
||||
// This has a worst case complexity of O((BLEND_RADUS*2)^2)
|
||||
// BLEND_RADIUS is in chunks (8 tiles)
|
||||
private static final int BLEND_RADIUS = 5;
|
||||
|
||||
// The maximum number of tiles that can be blended before becoming visibly discontinuous
|
||||
private static final int MAX_BLEND = (int) ((BLEND_RADIUS * 8) / BLEND_DISTRIBUTION);
|
||||
|
||||
private static final int PLANE_ALL = 0b1111;
|
||||
|
||||
private static final Pattern PATTERN = Pattern.compile("^[ \\t]*(?<expr>" +
|
||||
"//.*$|" + // //comment
|
||||
"m[ \\t]*(?<mrx>[0-9]+)[ \\t]+(?<mry>[0-9]+)|" + // m <rx> <ry>
|
||||
"r[ \\t]*(?<rx>[0-9]+)[ \\t]+(?<ry>[0-9]+)|" + // r <rx> <ry>
|
||||
"R[ \\t]*(?<rx1>[0-9]+)[ \\t]+(?<ry1>[0-9]+)[ \\t]+(?<rx2>[0-9]+)[ \\t]+(?<ry2>[0-9]+)|" + // R <rx1> <ry1> <rx2> <ry2>
|
||||
"c[ \\t]*(?<cx>[0-9-]+)[ \\t]+(?<cy>[0-9-]+)|" + // c <cx> <cy>
|
||||
"C[ \\t]*(?<cx1>[0-9-]+)[ \\t]+(?<cy1>[0-9-]+)[ \\t]+(?<cx2>[0-9-]+)[ \\t]+(?<cy2>[0-9-]+)|" + // C <cx1> <cy1> <cx2> <cy2>
|
||||
"#[ \\t]*(?<color>[0-9a-fA-F]{6}|[0-9a-fA-F]{3})|" + // #<RRGGBB> or #<RGB>
|
||||
"p[ \\t]*(?<plane>all|0?[ \\t]*1?[ \\t]*2?[ \\t]*3?)|" + // p all or p<1><2><3><4>
|
||||
"b[ \\t]*(?<blend>[0-9]+)|" + // b <blend>
|
||||
"bounds[ \\t]+(?<bx1>[0-9]+)[ \\t]+(?<by1>[0-9]+)[ \\t]+(?<bx2>[0-9]+)[ \\t]+(?<by2>[0-9]+)" + // bounds <x0> <y0> <x1> <y1>
|
||||
")[ \\t]*");
|
||||
|
||||
private final int[] chunks;
|
||||
private final int[] planeOverrides;
|
||||
|
||||
private final int x1;
|
||||
private final int y1;
|
||||
private final int x2;
|
||||
private final int y2;
|
||||
private final int stride;
|
||||
|
||||
public Skybox(InputStream is, String filename) throws IOException
|
||||
{
|
||||
this(new InputStreamReader(is), filename);
|
||||
}
|
||||
|
||||
public Skybox(Reader reader, String filename) throws IOException
|
||||
{
|
||||
int[] chunks = null;
|
||||
int[] planeOverrides = new int[64];
|
||||
int planeOverrideEnd = 0;
|
||||
int x1 = 0, y1 = 0, x2 = 0, y2 = 0, stride = 0;
|
||||
BufferedReader br = new BufferedReader(reader);
|
||||
int lineNo = 1;
|
||||
int color = 0;
|
||||
int plane = PLANE_ALL;
|
||||
int rx1 = 0, ry1 = 0, rx2 = 0, ry2 = 0;
|
||||
try
|
||||
{
|
||||
Matcher m = PATTERN.matcher("");
|
||||
for (String line; (line = br.readLine()) != null; lineNo++)
|
||||
{
|
||||
m.reset(line);
|
||||
int end = 0;
|
||||
for (; end < line.length(); )
|
||||
{
|
||||
m.region(end, line.length());
|
||||
if (!m.find())
|
||||
{
|
||||
throw new IllegalArgumentException("Unexpected: \"" + line.substring(end) + "\" (" + filename + ":" + lineNo + ")");
|
||||
}
|
||||
end = m.end();
|
||||
|
||||
String expr = m.group("expr");
|
||||
if (expr == null || expr.length() <= 0 || expr.startsWith("//"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (chunks == null)
|
||||
{
|
||||
if (!expr.startsWith("bounds"))
|
||||
{
|
||||
throw new IllegalArgumentException("Expceted bounds (" + filename + ":" + lineNo + ")");
|
||||
}
|
||||
x1 = Integer.parseInt(m.group("bx1")) * 8;
|
||||
y1 = Integer.parseInt(m.group("by1")) * 8;
|
||||
x2 = (Integer.parseInt(m.group("bx2")) + 1) * 8;
|
||||
y2 = (Integer.parseInt(m.group("by2")) + 1) * 8;
|
||||
stride = (x2 - x1);
|
||||
chunks = new int[stride * (y2 - y1)];
|
||||
Arrays.fill(chunks, -1);
|
||||
continue;
|
||||
}
|
||||
|
||||
char cha = expr.charAt(0);
|
||||
switch (cha)
|
||||
{
|
||||
case '#':
|
||||
String sColor = m.group("color");
|
||||
int scolor = Integer.parseInt(sColor, 16);
|
||||
int cr, cg, cb;
|
||||
if (sColor.length() == 3)
|
||||
{
|
||||
// Expand #RGB to #RRGGBB
|
||||
cr = scolor >> 8 & 0xF;
|
||||
cr |= cr << 4;
|
||||
cg = scolor >> 4 & 0xF;
|
||||
cg |= cg << 4;
|
||||
cb = scolor & 0xF;
|
||||
cb |= cb << 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
cr = scolor >> 16 & 0xFF;
|
||||
cg = scolor >> 8 & 0xFF;
|
||||
cb = scolor & 0xFF;
|
||||
}
|
||||
|
||||
// Convert to YCoCg24 because it produces less blending artifacts due
|
||||
// to mismatched skew rates
|
||||
// See: https://stackoverflow.com/questions/10566668/lossless-rgb-to-ycbcr-transformation
|
||||
byte cco = (byte) (cb - cr);
|
||||
byte tmp = (byte) (cr + (cco >> 1));
|
||||
byte ccg = (byte) (tmp - cg);
|
||||
byte cy = (byte) (cg + (ccg >> 1));
|
||||
|
||||
color = color & 0xFF000000 | (cy & 0xFF) << 16 | (cco & 0xFF) << 8 | (ccg & 0xFF);
|
||||
break;
|
||||
case 'b':
|
||||
int iblend = Integer.parseInt(m.group("blend"));
|
||||
if (iblend < 0)
|
||||
{
|
||||
throw new IllegalArgumentException("Blend must be >=0 (" + filename + ":" + lineNo + ")");
|
||||
}
|
||||
if (iblend > MAX_BLEND)
|
||||
{
|
||||
throw new IllegalArgumentException("Blend must be <= " + MAX_BLEND + " (" + filename + ":" + lineNo + ")");
|
||||
}
|
||||
color = color & 0x00FFFFFF | iblend << 24;
|
||||
break;
|
||||
case 'm':
|
||||
rx2 = rx1 = Integer.parseInt(m.group("mrx"));
|
||||
ry2 = ry1 = Integer.parseInt(m.group("mry"));
|
||||
break;
|
||||
case 'p':
|
||||
String planes = m.group("plane");
|
||||
if ("all".equals(planes))
|
||||
{
|
||||
plane = PLANE_ALL;
|
||||
}
|
||||
else
|
||||
{
|
||||
plane = 0;
|
||||
for (int i = 0; i < planes.length(); i++)
|
||||
{
|
||||
plane |= 1 << (planes.charAt(i) - '0');
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
case 'R':
|
||||
if (cha == 'r')
|
||||
{
|
||||
rx2 = rx1 = Integer.parseInt(m.group("rx"));
|
||||
ry2 = ry1 = Integer.parseInt(m.group("ry"));
|
||||
}
|
||||
else
|
||||
{
|
||||
rx1 = Integer.parseInt(m.group("rx1"));
|
||||
ry1 = Integer.parseInt(m.group("ry1"));
|
||||
rx2 = Integer.parseInt(m.group("rx2"));
|
||||
ry2 = Integer.parseInt(m.group("ry2"));
|
||||
}
|
||||
// fallthrough
|
||||
case 'c':
|
||||
case 'C':
|
||||
int cx1 = rx1 * 8;
|
||||
int cy1 = ry1 * 8;
|
||||
int cx2 = rx2 * 8 + 7;
|
||||
int cy2 = ry2 * 8 + 7;
|
||||
if (cha == 'c')
|
||||
{
|
||||
cx2 = cx1 = cx1 + Integer.parseInt(m.group("cx"));
|
||||
cy2 = cy1 = cy1 + Integer.parseInt(m.group("cy"));
|
||||
}
|
||||
else if (cha == 'C')
|
||||
{
|
||||
cx2 = cx1 + Integer.parseInt(m.group("cx2"));
|
||||
cy2 = cy1 + Integer.parseInt(m.group("cy2"));
|
||||
cx1 = cx1 + Integer.parseInt(m.group("cx1"));
|
||||
cy1 = cy1 + Integer.parseInt(m.group("cy1"));
|
||||
}
|
||||
|
||||
if (cx1 < x1 || cy1 < y1 || cx2 >= x2 || cy2 >= y2)
|
||||
{
|
||||
throw new IllegalArgumentException("Coordinate out of bounds (" + filename + ":" + lineNo + ")");
|
||||
}
|
||||
if (cx1 > cx2 || cy1 > cy2)
|
||||
{
|
||||
throw new IllegalArgumentException("First coord must be before second (" + filename + ":" + lineNo + ")");
|
||||
}
|
||||
|
||||
for (int y = cy1; y <= cy2; y++)
|
||||
{
|
||||
int yoffset = stride * (y - y1);
|
||||
for (int x = cx1; x <= cx2; x++)
|
||||
{
|
||||
int offset = (x - x1) + yoffset;
|
||||
|
||||
if (plane == PLANE_ALL)
|
||||
{
|
||||
chunks[offset] = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We are not setting all planes in this chunk, so allocate a plane override section
|
||||
// and add a pointer to it in the normal chunk's space. We do this because most chunks
|
||||
// do not have plane-specific data
|
||||
int ocv = chunks[offset];
|
||||
int poptr;
|
||||
if ((ocv & 0x8000_0000) != 0 && ocv != -1)
|
||||
{
|
||||
// Existing plane override
|
||||
poptr = ocv & 0x7FFF_FFFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
poptr = planeOverrideEnd;
|
||||
planeOverrideEnd += 4;
|
||||
if (planeOverrideEnd > planeOverrides.length)
|
||||
{
|
||||
planeOverrides = Arrays.copyOf(planeOverrides, planeOverrideEnd + 64);
|
||||
}
|
||||
chunks[offset] = poptr | 0x8000_0000;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
planeOverrides[poptr + i] = ocv;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if ((plane & (1 << i)) != 0)
|
||||
{
|
||||
planeOverrides[poptr + i] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (NumberFormatException ex)
|
||||
{
|
||||
throw new IllegalArgumentException("Expected number (" + filename + ":" + lineNo + ")", ex);
|
||||
}
|
||||
if (chunks == null)
|
||||
{
|
||||
throw new IllegalArgumentException(filename + ": no data");
|
||||
}
|
||||
|
||||
this.chunks = chunks;
|
||||
this.planeOverrides = planeOverrides;
|
||||
this.stride = stride;
|
||||
this.x1 = x1;
|
||||
this.y1 = y1;
|
||||
this.x2 = x2;
|
||||
this.y2 = y2;
|
||||
}
|
||||
|
||||
private int chunkData(int cx, int cy, int plane, ChunkMapper chunkMapper)
|
||||
{
|
||||
if (chunkMapper != null)
|
||||
{
|
||||
int itp = chunkMapper.getTemplateChunk(cx, cy, plane);
|
||||
if (itp == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
cy = itp >> 3 & 0x7FF;
|
||||
cx = itp >> 14 & 0x3FF;
|
||||
plane = itp >> 24 & 0x3;
|
||||
}
|
||||
|
||||
if (cx < x1)
|
||||
{
|
||||
cx = x1;
|
||||
}
|
||||
if (cx >= x2)
|
||||
{
|
||||
cx = x2 - 1;
|
||||
}
|
||||
if (cy < y1)
|
||||
{
|
||||
cy = y1;
|
||||
}
|
||||
if (cy >= y2)
|
||||
{
|
||||
cy = y2 - 1;
|
||||
}
|
||||
|
||||
int cv = chunks[(stride * (cy - y1)) + (cx - x1)];
|
||||
if (cv == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if ((cv & 0x8000_0000) != 0)
|
||||
{
|
||||
cv = planeOverrides[(cv & 0x7FFF_FFFF) | plane];
|
||||
}
|
||||
return cv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the RGB color for a specific world coordinate. Arguments are floats for sub-tile accuracy.
|
||||
*
|
||||
* @param x X coordinate in tiles
|
||||
* @param y Y coordinate in tiles
|
||||
* @param chunkMapper maps chunks to their instance templates, or null if not in an instance
|
||||
*/
|
||||
public int getColorForPoint(double x, double y, int plane, double brightness, ChunkMapper chunkMapper)
|
||||
{
|
||||
x /= 8.d;
|
||||
y /= 8.d;
|
||||
|
||||
int cx = (int) x;
|
||||
int cy = (int) y;
|
||||
|
||||
int centerChunkData = chunkData(cx, cy, plane, chunkMapper);
|
||||
if (centerChunkData == -1)
|
||||
{
|
||||
// No data in the center chunk?
|
||||
return 0;
|
||||
}
|
||||
|
||||
double t = 0;
|
||||
double ty = 0;
|
||||
double tco = 0;
|
||||
double tcg = 0;
|
||||
|
||||
int xmin = (int) (x - BLEND_RADIUS);
|
||||
int xmax = (int) Math.ceil(x + BLEND_RADIUS);
|
||||
int ymin = (int) (y - BLEND_RADIUS);
|
||||
int ymax = (int) Math.ceil(y + BLEND_RADIUS);
|
||||
|
||||
for (int ucx = xmin; ucx < xmax; ucx++)
|
||||
{
|
||||
for (int ucy = ymin; ucy <= ymax; ucy++)
|
||||
{
|
||||
int val = chunkData(ucx, ucy, plane, chunkMapper);
|
||||
if (val == -1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the blend value, add 1/8 tile to make sure we don't div/0, convert to chunks
|
||||
double sigma = ((val >>> 24) + .125) / 8.d;
|
||||
|
||||
// Calculate how far we have to be away before we can discard this value without
|
||||
// becoming visibly discontinuous
|
||||
double minDist = 1 + (sigma * BLEND_DISTRIBUTION);
|
||||
|
||||
// Try to fast-fail
|
||||
double dxl = ucx - x;
|
||||
double dxh = dxl + 1.d;
|
||||
if (dxl < -minDist || dxl > minDist)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
double dyl = ucy - y;
|
||||
double dyh = dyl + 1.d;
|
||||
if (dyl < -minDist || dyh > minDist)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculate integrate a gaussian distribution in each dimension for
|
||||
// this chunk relative to the requested point
|
||||
double erfdivc = sigma * SQRT2;
|
||||
double m = (erf(dxl / erfdivc) - erf(dxh / erfdivc)) * (erf(dyl / erfdivc) - erf(dyh / erfdivc));
|
||||
|
||||
// Load our YCoCg24 values into floats
|
||||
double vy = (val >>> 16 & 0xFF) / 255.d;
|
||||
double vco = (byte) (val >>> 8) / 128.d;
|
||||
double vcg = (byte) val / 128.d;
|
||||
|
||||
// And multiply by the weight
|
||||
ty += vy * m;
|
||||
tco += vco * m;
|
||||
tcg += vcg * m;
|
||||
t += m;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert back to int range values, and bounds check while we are at it
|
||||
byte ay = (byte) Math.min(Math.max(Math.round(Math.pow(ty / t, brightness) * 255.d), 0), 255);
|
||||
byte aco = (byte) Math.min(Math.max(Math.round(tco * 128.d / t), -128), 127);
|
||||
byte acg = (byte) Math.min(Math.max(Math.round(tcg * 128.d / t), -128), 127);
|
||||
|
||||
// convert back to rgb from YCoCg24
|
||||
int g = (ay - (acg >> 1)) & 0xFF;
|
||||
int tmp = (g + acg) & 0xFF;
|
||||
int r = (tmp - (aco >> 1)) & 0xFF;
|
||||
int b = (r + aco) & 0xFF;
|
||||
|
||||
return r << 16 | g << 8 | b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Approximation of erf 'Gauss error function' which is used to calculate
|
||||
* the cumulative distribution of a gaussian distribution.
|
||||
* This is used to simulate a large kernel gaussian blur without having
|
||||
* to sample the same chunk multiple times.
|
||||
*/
|
||||
private double erf(double x)
|
||||
{
|
||||
double ax = Math.abs(x);
|
||||
double t = 1.d / (1.d + (ax * .3275911d));
|
||||
double y = 1.d - ((((((1.061405429d * t) - 1.453152027d) * t) + 1.421413741d) * t - 0.284496736d) * t + 0.254829592d) * t * Math.exp(-ax * ax);
|
||||
return Math.copySign(y, x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the skybox map to an image
|
||||
*
|
||||
* @param resolution The number of pixels per tile
|
||||
* @param line How many tiles to put a line
|
||||
* @param plane the plane (0-4) to render
|
||||
*/
|
||||
BufferedImage render(double resolution, int line, int plane, ChunkMapper chunkMapper)
|
||||
{
|
||||
int w = (int) (((x2 - x1) * 8) * resolution);
|
||||
int h = (int) (((y2 - y1) * 8) * resolution);
|
||||
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
|
||||
int lineEvery = line <= 0 ? Integer.MAX_VALUE : (int) (line * resolution);
|
||||
|
||||
for (int y = 0; y < h; y++)
|
||||
{
|
||||
for (int x = 0; x < w; x++)
|
||||
{
|
||||
int color;
|
||||
if (x % lineEvery == 0 || y % lineEvery == 0)
|
||||
{
|
||||
color = 0x00FFFFFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
double fx = (x1 * 8) + (x / resolution);
|
||||
double fy = (y1 * 8) + (y / resolution);
|
||||
color = getColorForPoint(fx, fy, plane, .8, chunkMapper);
|
||||
}
|
||||
img.setRGB(x, h - 1 - y, color | 0xFF000000);
|
||||
}
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Abex
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.plugins.skybox;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.Player;
|
||||
import net.runelite.api.coords.LocalPoint;
|
||||
import net.runelite.api.events.BeforeRender;
|
||||
import net.runelite.api.events.GameStateChanged;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Skybox",
|
||||
description = "Draws a oldschool styled skybox",
|
||||
enabledByDefault = false,
|
||||
tags = {"sky"}
|
||||
)
|
||||
public class SkyboxPlugin extends Plugin
|
||||
{
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
private Skybox skybox;
|
||||
|
||||
@Override
|
||||
public void startUp() throws IOException
|
||||
{
|
||||
skybox = new Skybox(SkyboxPlugin.class.getResourceAsStream("skybox.txt"), "skybox.txt");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutDown()
|
||||
{
|
||||
client.setSkyboxColor(0);
|
||||
skybox = null;
|
||||
}
|
||||
|
||||
private int mapChunk(int cx, int cy, int plane)
|
||||
{
|
||||
cx -= client.getBaseX() / 8;
|
||||
cy -= client.getBaseY() / 8;
|
||||
|
||||
int[][] instanceTemplateChunks = client.getInstanceTemplateChunks()[plane];
|
||||
// Blending can access this out of bounds, so do a range check
|
||||
if (cx < 0 || cx >= instanceTemplateChunks.length || cy < 0 || cy >= instanceTemplateChunks[cx].length)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return instanceTemplateChunks[cx][cy];
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onBeforeRender(BeforeRender r)
|
||||
{
|
||||
if (skybox == null || client.getGameState() != GameState.LOGGED_IN)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = client.getLocalPlayer();
|
||||
if (player == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int px, py;
|
||||
if (client.getOculusOrbState() == 1)
|
||||
{
|
||||
px = client.getOculusOrbFocalPointX();
|
||||
py = client.getOculusOrbFocalPointY();
|
||||
}
|
||||
else
|
||||
{
|
||||
LocalPoint p = client.getLocalPlayer().getLocalLocation();
|
||||
px = p.getX();
|
||||
py = p.getY();
|
||||
}
|
||||
|
||||
// Inverse of camera location / 2
|
||||
int spx = px - ((client.getCameraX() - px) >> 1);
|
||||
int spy = py - ((client.getCameraY() - py) >> 1);
|
||||
|
||||
client.setSkyboxColor(skybox.getColorForPoint(
|
||||
client.getBaseX() + (spx / 128.f),
|
||||
client.getBaseY() + (spy / 128.f),
|
||||
client.getPlane(),
|
||||
client.getTextureProvider().getBrightness(),
|
||||
client.isInInstancedRegion() ? this::mapChunk : null
|
||||
));
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameStateChanged(GameStateChanged gameStateChanged)
|
||||
{
|
||||
if (gameStateChanged.getGameState() == GameState.LOGIN_SCREEN)
|
||||
{
|
||||
client.setSkyboxColor(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user